ontowave 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3057 @@
1
+ /**
2
+ * OntoWave - Documentation Interactive Package
3
+ * Version: 1.0.0
4
+ * URL: https://github.com/stephanedenis/OntoWave
5
+ *
6
+ * Usage simple:
7
+ * <script src="https://cdn.jsdelivr.net/npm/ontowave@latest/dist/ontowave.min.js"></script>
8
+ * <script type="application/json" id="ontowave-config">
9
+ * {
10
+ * "title": "Ma Documentation",
11
+ * "baseUrl": "/",
12
+ * "defaultPage": "index.md",
13
+ * "mermaid": { "theme": "default" },
14
+ * "plantuml": { "server": "https://www.plantuml.com/plantuml" }
15
+ * }
16
+ * </script>
17
+ */
18
+
19
+ (function(window) {
20
+ 'use strict';
21
+
22
+ // Traductions pour l'interface
23
+ const TRANSLATIONS = {
24
+ fr: {
25
+ // Menu
26
+ menuHome: "Accueil",
27
+ menuGallery: "Galerie",
28
+ menuConfiguration: "Configuration",
29
+
30
+ // Panneau de configuration
31
+ configTitle: "OntoWave - Configuration Complète",
32
+ configGeneral: "Général",
33
+ configSiteTitle: "Titre du site :",
34
+ configDefaultPage: "Page par défaut :",
35
+ configBaseUrl: "URL de base :",
36
+ configLanguages: "Langues et Localisation",
37
+ configSupportedLanguages: "Langues supportées (séparées par des virgules) :",
38
+ configFallbackLanguage: "Langue de fallback :",
39
+ configNavigation: "Navigation et Interface",
40
+ configShowGallery: "Afficher la galerie d'exemples",
41
+ configHomeButton: "Bouton Accueil",
42
+ configBreadcrumb: "Fil d'Ariane (breadcrumb)",
43
+ configToc: "Table des matières",
44
+ configMermaid: "Diagrammes Mermaid",
45
+ configMermaidTheme: "Thème Mermaid :",
46
+ configMermaidAuto: "Démarrage automatique",
47
+ configMermaidMaxWidth: "Utiliser la largeur maximale",
48
+ configPlantuml: "Diagrammes PlantUML",
49
+ configPlantumlServer: "Serveur PlantUML :",
50
+ configPlantumlFormat: "Format de sortie :",
51
+ configPrism: "Coloration Syntaxique (Prism.js)",
52
+ configPrismTheme: "Thème Prism :",
53
+ configPrismAutoload: "Chargement automatique",
54
+ configUI: "Interface Utilisateur",
55
+ configUITheme: "Thème de l'interface :",
56
+ configUIResponsive: "Design responsive",
57
+ configUIAnimations: "Animations et transitions",
58
+ configApply: "Appliquer Configuration",
59
+ configDownloadHTML: "Télécharger HTML",
60
+ configDownloadJS: "Télécharger ontowave.min.js",
61
+ configReset: "Réinitialiser",
62
+ configLanguageNote: "Laissez vide pour un site monolingue"
63
+ },
64
+ en: {
65
+ // Menu
66
+ menuHome: "Home",
67
+ menuGallery: "Gallery",
68
+ menuConfiguration: "Configuration",
69
+
70
+ // Configuration Panel
71
+ configTitle: "OntoWave - Complete Configuration",
72
+ configGeneral: "General",
73
+ configSiteTitle: "Site title:",
74
+ configDefaultPage: "Default page:",
75
+ configBaseUrl: "Base URL:",
76
+ configLanguages: "Languages and Localization",
77
+ configSupportedLanguages: "Supported languages (comma separated):",
78
+ configFallbackLanguage: "Fallback language:",
79
+ configNavigation: "Navigation and Interface",
80
+ configShowGallery: "Show examples gallery",
81
+ configHomeButton: "Home button",
82
+ configBreadcrumb: "Breadcrumb navigation",
83
+ configToc: "Table of contents",
84
+ configMermaid: "Mermaid Diagrams",
85
+ configMermaidTheme: "Mermaid theme:",
86
+ configMermaidAuto: "Auto start",
87
+ configMermaidMaxWidth: "Use maximum width",
88
+ configPlantuml: "PlantUML Diagrams",
89
+ configPlantumlServer: "PlantUML server:",
90
+ configPlantumlFormat: "Output format:",
91
+ configPrism: "Syntax Highlighting (Prism.js)",
92
+ configPrismTheme: "Prism theme:",
93
+ configPrismAutoload: "Auto loading",
94
+ configUI: "User Interface",
95
+ configUITheme: "Interface theme:",
96
+ configUIResponsive: "Responsive design",
97
+ configUIAnimations: "Animations and transitions",
98
+ configApply: "Apply Configuration",
99
+ configDownloadHTML: "Download HTML",
100
+ configDownloadJS: "Download ontowave.min.js",
101
+ configReset: "Reset",
102
+ configLanguageNote: "Leave empty for monolingual site"
103
+ }
104
+ };
105
+
106
+ // Configuration par défaut
107
+ const DEFAULT_CONFIG = {
108
+ title: "OntoWave Documentation",
109
+ baseUrl: "/",
110
+ defaultPage: "index.md",
111
+ containerId: "ontowave-container",
112
+ locales: ["fr", "en"], // Langues supportées par défaut
113
+ fallbackLocale: "en",
114
+ showGallery: false, // Gallerie désactivée par défaut
115
+ mermaid: {
116
+ theme: "default",
117
+ startOnLoad: true,
118
+ flowchart: { useMaxWidth: true },
119
+ sequence: { useMaxWidth: true },
120
+ gantt: { useMaxWidth: true },
121
+ journey: { useMaxWidth: true }
122
+ },
123
+ plantuml: {
124
+ server: "https://www.plantuml.com/plantuml",
125
+ format: "svg"
126
+ },
127
+ prism: {
128
+ theme: "default",
129
+ autoload: true
130
+ },
131
+ navigation: {
132
+ showHome: true,
133
+ showBreadcrumb: true,
134
+ showToc: true
135
+ },
136
+ ui: {
137
+ theme: "default",
138
+ responsive: true,
139
+ animations: true,
140
+ languageButtons: "menu" // "fixed", "menu", "both"
141
+ }
142
+ };
143
+
144
+ // Styles CSS intégrés
145
+ const CSS_STYLES = `
146
+ .ontowave-container {
147
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
148
+ line-height: 1.6;
149
+ color: #24292e;
150
+ background: #fff;
151
+ margin: 0;
152
+ padding: 20px;
153
+ min-height: 100vh;
154
+ }
155
+
156
+ /* Menu flottant minimal déplaçable */
157
+ .ontowave-floating-menu {
158
+ position: fixed;
159
+ top: 20px;
160
+ left: 20px;
161
+ z-index: 1000;
162
+ background: rgba(255, 255, 255, 0.95);
163
+ backdrop-filter: blur(10px);
164
+ border: 1px solid #e1e4e8;
165
+ border-radius: 44px;
166
+ padding: 10px;
167
+ box-shadow: 0 4px 12px rgba(27,31,35,0.15);
168
+ cursor: move;
169
+ transition: all 0.3s ease;
170
+ font-size: 1.2em;
171
+ user-select: none;
172
+ display: flex;
173
+ align-items: center;
174
+ gap: 0;
175
+ width: 44px;
176
+ height: 44px;
177
+ overflow: visible;
178
+ white-space: nowrap;
179
+ }
180
+
181
+ .ontowave-floating-menu.no-drag {
182
+ cursor: default;
183
+ }
184
+
185
+ .ontowave-floating-menu.expanded {
186
+ width: auto;
187
+ height: auto;
188
+ min-height: 44px;
189
+ border-radius: 22px;
190
+ padding: 10px 18px;
191
+ gap: 10px;
192
+ }
193
+
194
+ .ontowave-floating-menu:hover {
195
+ transform: scale(1.05);
196
+ box-shadow: 0 6px 20px rgba(27,31,35,0.25);
197
+ }
198
+
199
+ /* Désactiver le zoom quand le panneau de configuration est ouvert */
200
+ .ontowave-floating-menu.has-config-panel:hover {
201
+ transform: none;
202
+ }
203
+
204
+ .ontowave-menu-icon {
205
+ cursor: pointer;
206
+ transition: transform 0.3s ease;
207
+ flex-shrink: 0;
208
+ width: 30px;
209
+ height: 30px;
210
+ display: flex;
211
+ align-items: center;
212
+ justify-content: center;
213
+ font-size: 1.4em;
214
+ text-align: center;
215
+ line-height: 1;
216
+ margin: 0 auto;
217
+ }
218
+
219
+ .ontowave-menu-icon:hover {
220
+ transform: scale(1.2);
221
+ }
222
+
223
+ .ontowave-menu-content {
224
+ display: flex;
225
+ align-items: center;
226
+ gap: 15px;
227
+ opacity: 0;
228
+ width: 0;
229
+ overflow: hidden;
230
+ transition: all 0.3s ease;
231
+ white-space: nowrap;
232
+ }
233
+
234
+ .ontowave-floating-menu.expanded .ontowave-menu-content {
235
+ opacity: 1;
236
+ width: auto;
237
+ overflow: visible;
238
+ }
239
+
240
+ .ontowave-menu-brand {
241
+ font-weight: 600;
242
+ color: #0969da;
243
+ text-decoration: none;
244
+ cursor: pointer;
245
+ font-size: 0.9em;
246
+ }
247
+
248
+ .ontowave-menu-brand:hover {
249
+ color: #0550ae;
250
+ }
251
+
252
+ .org-suffix {
253
+ font-size: 0.7em;
254
+ opacity: 0.7;
255
+ font-weight: normal;
256
+ }
257
+
258
+ .ontowave-menu-options {
259
+ display: flex;
260
+ gap: 8px;
261
+ flex-wrap: nowrap;
262
+ }
263
+
264
+ .ontowave-menu-option {
265
+ padding: 6px 10px;
266
+ background: #f8f9fa;
267
+ border: 1px solid #d0d7de;
268
+ border-radius: 15px;
269
+ font-size: 0.75em;
270
+ cursor: pointer;
271
+ transition: all 0.2s ease;
272
+ white-space: nowrap;
273
+ pointer-events: auto;
274
+ }
275
+
276
+ .ontowave-menu-option:hover {
277
+ background: #e2e8f0;
278
+ transform: translateY(-1px);
279
+ }
280
+
281
+ .ontowave-menu-option.selected {
282
+ background: #0969da;
283
+ color: white;
284
+ border-color: #0969da;
285
+ box-shadow: 0 2px 8px rgba(9, 105, 218, 0.3);
286
+ }
287
+
288
+ .ontowave-menu-option.selected:hover {
289
+ background: #0550ae;
290
+ border-color: #0550ae;
291
+ transform: translateY(-1px);
292
+ }
293
+
294
+ /* Styles pour les boutons de langue */
295
+ .ontowave-lang-btn {
296
+ font-weight: bold;
297
+ font-size: 11px;
298
+ }
299
+
300
+ .ontowave-lang-btn.active {
301
+ background: #28a745;
302
+ color: white;
303
+ border-color: #28a745;
304
+ box-shadow: 0 2px 8px rgba(40, 167, 69, 0.3);
305
+ }
306
+
307
+ .ontowave-lang-btn.active:hover {
308
+ background: #1e7e34;
309
+ border-color: #1e7e34;
310
+ }
311
+
312
+ /* Boutons de langue fixés - nouveaux styles */
313
+ .ontowave-fixed-lang-buttons {
314
+ position: fixed;
315
+ top: 20px;
316
+ right: 20px;
317
+ z-index: 999;
318
+ display: flex;
319
+ gap: 8px;
320
+ background: rgba(255, 255, 255, 0.95);
321
+ padding: 8px 12px;
322
+ border-radius: 25px;
323
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
324
+ backdrop-filter: blur(10px);
325
+ border: 1px solid rgba(255, 255, 255, 0.2);
326
+ }
327
+
328
+ /* Responsive : adaptation mobile */
329
+ @media (max-width: 768px) {
330
+ .ontowave-fixed-lang-buttons {
331
+ top: 10px;
332
+ right: 10px;
333
+ padding: 6px 8px;
334
+ gap: 4px;
335
+ }
336
+ }
337
+
338
+ .ontowave-fixed-lang-btn {
339
+ background: linear-gradient(135deg, #6c757d 0%, #495057 100%);
340
+ color: white;
341
+ border: none;
342
+ padding: 6px 12px;
343
+ border-radius: 15px;
344
+ font-size: 12px;
345
+ font-weight: bold;
346
+ cursor: pointer;
347
+ transition: all 0.3s ease;
348
+ text-decoration: none;
349
+ display: flex;
350
+ align-items: center;
351
+ gap: 4px;
352
+ min-width: 40px;
353
+ justify-content: center;
354
+ }
355
+
356
+ /* Responsive : boutons plus petits sur mobile */
357
+ @media (max-width: 768px) {
358
+ .ontowave-fixed-lang-btn {
359
+ padding: 4px 8px;
360
+ font-size: 11px;
361
+ min-width: 35px;
362
+ gap: 2px;
363
+ }
364
+ }
365
+
366
+ .ontowave-fixed-lang-btn:hover {
367
+ background: linear-gradient(135deg, #5a6268 0%, #343a40 100%);
368
+ transform: translateY(-2px);
369
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
370
+ }
371
+
372
+ .ontowave-fixed-lang-btn.active {
373
+ background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
374
+ box-shadow: 0 4px 12px rgba(40, 167, 69, 0.4);
375
+ }
376
+
377
+ .ontowave-fixed-lang-btn.active:hover {
378
+ background: linear-gradient(135deg, #1e7e34 0%, #198754 100%);
379
+ transform: translateY(-2px);
380
+ }
381
+
382
+ /* Pas d'en-tête - supprimé */
383
+ .ontowave-header {
384
+ display: none;
385
+ }
386
+
387
+ .ontowave-nav {
388
+ background: #f8f9fa;
389
+ border: 1px solid #e1e4e8;
390
+ border-radius: 8px;
391
+ padding: 1.5rem;
392
+ margin-bottom: 2rem;
393
+ }
394
+
395
+ .ontowave-nav h3 {
396
+ margin: 0 0 1rem 0;
397
+ color: #24292e;
398
+ display: flex;
399
+ align-items: center;
400
+ gap: 0.5rem;
401
+ }
402
+
403
+ .ontowave-nav-grid {
404
+ display: grid;
405
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
406
+ gap: 1rem;
407
+ }
408
+
409
+ .ontowave-nav-item {
410
+ padding: 1rem;
411
+ background: white;
412
+ border: 1px solid #d0d7de;
413
+ border-radius: 6px;
414
+ text-decoration: none;
415
+ color: #24292e;
416
+ display: flex;
417
+ align-items: center;
418
+ gap: 0.5rem;
419
+ transition: all 0.2s ease;
420
+ font-weight: 500;
421
+ cursor: pointer;
422
+ }
423
+
424
+ .ontowave-nav-item:hover {
425
+ background: #f3f4f6;
426
+ border-color: #0969da;
427
+ transform: translateY(-2px);
428
+ box-shadow: 0 4px 12px rgba(27,31,35,0.15);
429
+ }
430
+
431
+ .ontowave-content {
432
+ background: white;
433
+ padding: 2rem;
434
+ border-radius: 8px;
435
+ box-shadow: 0 2px 8px rgba(27,31,35,0.15);
436
+ border: 1px solid #e1e4e8;
437
+ margin-bottom: 2rem;
438
+ min-height: 300px;
439
+ max-width: 1200px;
440
+ margin-left: auto;
441
+ margin-right: auto;
442
+ }
443
+
444
+ /* Headers corrigés */
445
+ .ontowave-content h1 {
446
+ color: #24292e;
447
+ border-bottom: 2px solid #e1e4e8;
448
+ padding-bottom: 8px;
449
+ margin-bottom: 16px;
450
+ font-size: 2em;
451
+ font-weight: 600;
452
+ }
453
+
454
+ .ontowave-content h2 {
455
+ color: #24292e;
456
+ margin-top: 24px;
457
+ margin-bottom: 16px;
458
+ font-size: 1.5em;
459
+ font-weight: 600;
460
+ }
461
+
462
+ .ontowave-content h3 {
463
+ color: #24292e;
464
+ margin-top: 20px;
465
+ margin-bottom: 12px;
466
+ font-size: 1.25em;
467
+ font-weight: 600;
468
+ }
469
+
470
+ .ontowave-content h4 {
471
+ color: #24292e;
472
+ margin-top: 16px;
473
+ margin-bottom: 10px;
474
+ font-size: 1.1em;
475
+ font-weight: 600;
476
+ }
477
+
478
+ .ontowave-content h5 {
479
+ color: #24292e;
480
+ margin-top: 14px;
481
+ margin-bottom: 8px;
482
+ font-size: 1em;
483
+ font-weight: 600;
484
+ }
485
+
486
+ .ontowave-content h6 {
487
+ color: #24292e;
488
+ margin-top: 12px;
489
+ margin-bottom: 6px;
490
+ font-size: 0.9em;
491
+ font-weight: 600;
492
+ }
493
+
494
+ /* Séparateurs hr */
495
+ .ontowave-content hr {
496
+ border: none;
497
+ border-top: 1px solid #e1e4e8;
498
+ margin: 24px 0;
499
+ }
500
+
501
+ /* Mermaid diagrams */
502
+ .ontowave-mermaid {
503
+ margin: 20px 0;
504
+ padding: 20px;
505
+ background: #f8f9fa;
506
+ border: 1px solid #e1e4e8;
507
+ border-radius: 8px;
508
+ text-align: center;
509
+ }
510
+
511
+ .ontowave-loading {
512
+ text-align: center;
513
+ color: #666;
514
+ padding: 2rem;
515
+ }
516
+
517
+ .ontowave-error {
518
+ color: #d73a49;
519
+ text-align: center;
520
+ padding: 2rem;
521
+ background: #ffeef0;
522
+ border: 1px solid #fdaeb7;
523
+ border-radius: 6px;
524
+ }
525
+
526
+ .ontowave-mermaid {
527
+ background: #f6f8fa;
528
+ border: 1px solid #d0d7de;
529
+ border-radius: 6px;
530
+ padding: 1rem;
531
+ margin: 1rem 0;
532
+ text-align: center;
533
+ }
534
+
535
+ .ontowave-plantuml {
536
+ background: #f6f8fa;
537
+ border: 1px solid #d0d7de;
538
+ border-radius: 6px;
539
+ padding: 1rem;
540
+ margin: 1rem 0;
541
+ text-align: center;
542
+ }
543
+
544
+ .ontowave-plantuml img {
545
+ max-width: 100%;
546
+ height: auto;
547
+ }
548
+
549
+ .ontowave-code {
550
+ background: #f6f8fa;
551
+ border: 1px solid #d0d7de;
552
+ border-radius: 6px;
553
+ padding: 1rem;
554
+ margin: 1rem 0;
555
+ overflow-x: auto;
556
+ font-family: ui-monospace, SFMono-Regular, monospace;
557
+ }
558
+
559
+ .ontowave-breadcrumb {
560
+ padding: 0.5rem 0;
561
+ margin-bottom: 1rem;
562
+ border-bottom: 1px solid #e1e4e8;
563
+ }
564
+
565
+ .ontowave-breadcrumb a {
566
+ color: #0969da;
567
+ text-decoration: none;
568
+ margin-right: 0.5rem;
569
+ }
570
+
571
+ .ontowave-breadcrumb a:hover {
572
+ text-decoration: underline;
573
+ }
574
+
575
+ .ontowave-breadcrumb span {
576
+ color: #656d76;
577
+ margin-right: 0.5rem;
578
+ }
579
+
580
+ .ontowave-status {
581
+ background: #d4edda;
582
+ border: 1px solid #c3e6cb;
583
+ border-radius: 8px;
584
+ padding: 1rem;
585
+ margin-top: 2rem;
586
+ }
587
+
588
+ .ontowave-status h4 {
589
+ margin: 0 0 0.5rem 0;
590
+ color: #155724;
591
+ }
592
+
593
+ .ontowave-status ul {
594
+ margin: 0;
595
+ padding-left: 1.5rem;
596
+ color: #155724;
597
+ }
598
+
599
+ @media (max-width: 768px) {
600
+ .ontowave-header {
601
+ padding: 1rem;
602
+ }
603
+
604
+ .ontowave-header h1 {
605
+ font-size: 2em;
606
+ }
607
+
608
+ .ontowave-nav-grid {
609
+ grid-template-columns: 1fr;
610
+ }
611
+
612
+ .ontowave-content {
613
+ padding: 1rem;
614
+ }
615
+ }
616
+ `;
617
+
618
+ class OntoWave {
619
+ constructor(config = {}) {
620
+ this.config = { ...DEFAULT_CONFIG, ...config };
621
+
622
+ // Support pour le format i18n (compatibilité avec config.json)
623
+ if (config.i18n) {
624
+ if (config.i18n.supported) {
625
+ this.config.locales = config.i18n.supported;
626
+ }
627
+ if (config.i18n.default) {
628
+ this.config.fallbackLocale = config.i18n.default;
629
+ }
630
+ }
631
+
632
+ this.container = null;
633
+ this.mermaidLoaded = false;
634
+ this.prismLoaded = false;
635
+ this.currentPage = null;
636
+ this.currentLanguage = null; // Langue courante stockée
637
+ }
638
+
639
+ /**
640
+ * Détecte la langue actuelle de l'interface
641
+ */
642
+ getCurrentLanguage() {
643
+ // Si une langue a été explicitement définie, l'utiliser
644
+ if (this.currentLanguage) {
645
+ return this.currentLanguage;
646
+ }
647
+
648
+ // Vérifier s'il y a une langue sélectionnée dans l'interface
649
+ const langFr = document.getElementById('lang-fr');
650
+ const langEn = document.getElementById('lang-en');
651
+
652
+ if (langFr && langEn) {
653
+ // Système multilingue détecté - vérifier les classes visible/hidden
654
+ if (langFr.classList.contains('visible') || (!langFr.classList.contains('hidden') && langFr.style.display !== 'none')) {
655
+ return 'fr';
656
+ } else if (langEn.classList.contains('visible') || (!langEn.classList.contains('hidden') && langEn.style.display !== 'none')) {
657
+ return 'en';
658
+ }
659
+ }
660
+
661
+ // Fallback - vérifier les boutons actifs
662
+ const btnFr = document.getElementById('btn-fr');
663
+ const btnEn = document.getElementById('btn-en');
664
+
665
+ if (btnFr && btnEn) {
666
+ if (btnFr.classList.contains('active')) {
667
+ return 'fr';
668
+ } else if (btnEn.classList.contains('active')) {
669
+ return 'en';
670
+ }
671
+ }
672
+
673
+ // Fallback sur les préférences du navigateur
674
+ return this.resolveLocale() || 'en';
675
+ }
676
+
677
+ /**
678
+ * Obtient une traduction pour une langue spécifique ou la langue actuelle
679
+ */
680
+ t(key, locale = null) {
681
+ const targetLang = locale || this.getCurrentLanguage();
682
+ const translations = TRANSLATIONS[targetLang] || TRANSLATIONS['en'];
683
+ return translations[key] || key;
684
+ }
685
+
686
+ /**
687
+ * Met à jour tous les textes de l'interface selon une langue spécifique
688
+ */
689
+ updateInterfaceTexts(locale = null) {
690
+ const targetLang = locale || this.getCurrentLanguage();
691
+ console.log('🌐 Interface texts updating for language:', targetLang);
692
+
693
+ // Mettre à jour les textes du menu
694
+ const homeOption = document.querySelector('.ontowave-menu-option[onclick*="goHome"]');
695
+ if (homeOption) {
696
+ homeOption.innerHTML = `🏠 ${this.t('menuHome', targetLang)}`;
697
+ }
698
+
699
+ const galleryOption = document.querySelector('.ontowave-menu-option[onclick*="gallery.html"]');
700
+ if (galleryOption) {
701
+ galleryOption.innerHTML = `🎨 ${this.t('menuGallery', targetLang)}`;
702
+ }
703
+
704
+ const configOption = document.querySelector('.ontowave-menu-option[onclick*="toggleConfigurationPanel"]');
705
+ if (configOption) {
706
+ configOption.innerHTML = `⚙️ ${this.t('menuConfiguration', targetLang)}`;
707
+ }
708
+
709
+ // Si le panneau de configuration est ouvert, le recréer avec les nouveaux textes
710
+ const configPanel = document.getElementById('ontowave-config-panel');
711
+ if (configPanel) {
712
+ // Sauvegarder l'état des champs avant de recréer le panneau
713
+ const titleValue = document.getElementById('config-title-full')?.value || this.config.title;
714
+ const defaultPageValue = document.getElementById('config-defaultPage-full')?.value || this.config.defaultPage;
715
+ const baseUrlValue = document.getElementById('config-baseUrl-full')?.value || this.config.baseUrl;
716
+
717
+ // Fermer et rouvrir le panneau pour le mettre à jour
718
+ configPanel.remove();
719
+ const configButton = document.querySelector('.ontowave-menu-option[onclick*="toggleConfigurationPanel"]');
720
+ if (configButton) {
721
+ configButton.classList.remove('selected');
722
+ }
723
+
724
+ // Rouvrir avec les nouvelles traductions et la langue spécifiée
725
+ setTimeout(() => {
726
+ this.toggleConfigurationPanel(null, targetLang);
727
+
728
+ // Restaurer les valeurs des champs
729
+ setTimeout(() => {
730
+ const titleField = document.getElementById('config-title-full');
731
+ const defaultPageField = document.getElementById('config-defaultPage-full');
732
+ const baseUrlField = document.getElementById('config-baseUrl-full');
733
+
734
+ if (titleField) titleField.value = titleValue;
735
+ if (defaultPageField) defaultPageField.value = defaultPageValue;
736
+ if (baseUrlField) baseUrlField.value = baseUrlValue;
737
+ }, 100);
738
+ }, 50);
739
+ }
740
+
741
+ console.log('🌐 Interface texts updated for language:', targetLang);
742
+ }
743
+
744
+ /**
745
+ * Change la langue de l'interface et recharge le contenu approprié
746
+ */
747
+ switchLanguage(targetLang) {
748
+ // Stocker la langue courante
749
+ this.currentLanguage = targetLang;
750
+
751
+ // Mettre à jour l'état des boutons de langue
752
+ this.updateLanguageButtonsState(targetLang);
753
+
754
+ // Mettre à jour l'interface
755
+ this.updateInterfaceTexts(targetLang);
756
+
757
+ // Recharger la page avec la bonne langue
758
+ const sources = this.config.sources || {};
759
+ const targetPage = sources[targetLang] || this.config.defaultPage;
760
+
761
+ if (targetPage) {
762
+ this.loadPage(targetPage);
763
+ }
764
+ }
765
+
766
+ /**
767
+ * Met à jour l'état visuel des boutons de langue pour refléter la langue actuelle
768
+ */
769
+ updateLanguageButtonsState(currentLang = null) {
770
+ const lang = currentLang || this.getCurrentLanguage();
771
+
772
+ // Mettre à jour les boutons de langue dans le menu
773
+ document.querySelectorAll('.ontowave-lang-btn').forEach(btn => {
774
+ btn.classList.remove('active');
775
+ if (btn.textContent.includes(lang.toUpperCase())) {
776
+ btn.classList.add('active');
777
+ }
778
+ });
779
+
780
+ // Mettre à jour les boutons de langue fixés
781
+ document.querySelectorAll('.ontowave-fixed-lang-btn').forEach(btn => {
782
+ btn.classList.remove('active');
783
+ if (btn.textContent.includes(lang.toUpperCase())) {
784
+ btn.classList.add('active');
785
+ }
786
+ });
787
+
788
+ console.log('🌐 État des boutons de langue mis à jour pour:', lang);
789
+ }
790
+
791
+ /**
792
+ * Charge la page d'accueil dans la langue courante
793
+ */
794
+ goHome() {
795
+ const currentLang = this.getCurrentLanguage();
796
+ const sources = this.config.sources || {};
797
+ const homePage = sources[currentLang] || this.config.defaultPage;
798
+ this.loadPage(homePage);
799
+ }
800
+
801
+ /**
802
+ * Résout les langues du navigateur par ordre de préférence
803
+ */
804
+ getBrowserLocales() {
805
+ const languages = [];
806
+
807
+ // navigator.languages (préférences utilisateur)
808
+ if (navigator.languages) {
809
+ languages.push(...navigator.languages);
810
+ }
811
+
812
+ // Fallbacks
813
+ if (navigator.language) {
814
+ languages.push(navigator.language);
815
+ }
816
+ if (navigator.userLanguage) {
817
+ languages.push(navigator.userLanguage);
818
+ }
819
+
820
+ return [...new Set(languages)]; // Enlever doublons
821
+ }
822
+
823
+ /**
824
+ * Trouve la meilleure correspondance entre langues navigateur et config
825
+ * Priorise maintenant le contexte de la page, puis les préférences du navigateur
826
+ */
827
+ resolveLocale() {
828
+ const browserLocales = this.getBrowserLocales();
829
+ const supportedLocales = this.config.locales || [];
830
+ const defaultLocale = this.config.defaultLocale || this.config.fallbackLocale;
831
+
832
+ console.log('🌐 Browser locales:', browserLocales);
833
+ console.log('🌐 Supported locales:', supportedLocales);
834
+ console.log('🌐 Default locale:', defaultLocale);
835
+
836
+ if (supportedLocales.length === 0) {
837
+ return null; // Mode monolingue
838
+ }
839
+
840
+ // PRIORITÉ 1 : Détecter la langue depuis l'URL actuelle
841
+ const currentUrl = window.location.hash || window.location.pathname;
842
+ console.log('🔍 Current URL:', currentUrl);
843
+
844
+ for (const locale of supportedLocales) {
845
+ // Rechercher des patterns comme index.fr.md, about.en.md, etc.
846
+ const langPattern = new RegExp(`\\.${locale}\\.(md|html)`);
847
+ if (langPattern.test(currentUrl)) {
848
+ console.log('🎯 Page language detected from URL:', locale, 'in', currentUrl);
849
+ return locale;
850
+ }
851
+ }
852
+
853
+ // PRIORITÉ 2 : Recherche exacte dans les langues navigateur
854
+ for (const browserLang of browserLocales) {
855
+ if (supportedLocales.includes(browserLang)) {
856
+ console.log('🎯 Exact browser match found:', browserLang);
857
+ return browserLang;
858
+ }
859
+ }
860
+
861
+ // PRIORITÉ 2 : Recherche par préfixe (fr-CA -> fr)
862
+ for (const browserLang of browserLocales) {
863
+ const prefix = browserLang.split('-')[0];
864
+
865
+ // PRIORITÉ 3 : Recherche par préfixe (fr-CA -> fr)
866
+ for (const browserLang of browserLocales) {
867
+ const prefix = browserLang.split('-')[0];
868
+ const match = supportedLocales.find(locale => locale.startsWith(prefix));
869
+ if (match) {
870
+ console.log('🎯 Prefix match found:', browserLang, '->', match);
871
+ return match;
872
+ }
873
+ }
874
+
875
+ // PRIORITÉ 4 : Utiliser defaultLocale si défini et supporté
876
+ if (defaultLocale && supportedLocales.includes(defaultLocale)) {
877
+ console.log('🎯 Using configured default locale:', defaultLocale);
878
+ return defaultLocale;
879
+ }
880
+
881
+ // PRIORITÉ 5 : Fallback sur la première langue supportée
882
+ }
883
+
884
+ // PRIORITÉ 4 : Fallback sur la première langue supportée
885
+ const fallback = supportedLocales[0];
886
+ console.log('🎯 Using fallback locale:', fallback);
887
+ return fallback;
888
+ }
889
+
890
+ /**
891
+ * Détermine si OntoWave est en mode multilingue
892
+ */
893
+ isMultilingualMode() {
894
+ return this.config.locales && this.config.locales.length > 0 && this.config.sources;
895
+ }
896
+
897
+ /**
898
+ * Génère la liste des fichiers à essayer pour une page donnée
899
+ */
900
+ generatePageCandidates(basePage) {
901
+ const resolvedLocale = this.resolveLocale();
902
+ const candidates = [];
903
+
904
+ if (!resolvedLocale) {
905
+ // Mode monolingue - essayer la page directement
906
+ candidates.push(basePage);
907
+ return candidates;
908
+ }
909
+
910
+ const pageName = basePage.replace(/\.md$/, '');
911
+
912
+ // Essayer avec la locale résolue
913
+ candidates.push(`${pageName}.${resolvedLocale}.md`);
914
+
915
+ // Si c'est une locale composée (fr-CA), essayer le préfixe
916
+ if (resolvedLocale.includes('-')) {
917
+ const prefix = resolvedLocale.split('-')[0];
918
+ candidates.push(`${pageName}.${prefix}.md`);
919
+ }
920
+
921
+ // Fallback sur la page de base
922
+ candidates.push(basePage);
923
+
924
+ console.log('📄 Page candidates for', basePage, ':', candidates);
925
+ return candidates;
926
+ }
927
+
928
+ async init() {
929
+ try {
930
+ // Charger la configuration depuis le script JSON si disponible
931
+ await this.loadConfigFromScript();
932
+
933
+ // Injecter les styles CSS
934
+ this.injectStyles();
935
+
936
+ // Charger Mermaid si nécessaire
937
+ await this.loadMermaid();
938
+
939
+ // Charger Prism si nécessaire
940
+ await this.loadPrism();
941
+
942
+ // Créer l'interface
943
+ this.createInterface();
944
+
945
+ // Initialiser la langue courante
946
+ this.currentLanguage = this.resolveLocale();
947
+
948
+ // Initialiser la navigation
949
+ this.initializeNavigation();
950
+
951
+ // Mettre à jour l'état des boutons de langue après la création du menu
952
+ this.updateLanguageButtonsState();
953
+
954
+ // Charger la page initiale
955
+ await this.loadInitialPage();
956
+
957
+ console.log('✅ OntoWave successfully initialized');
958
+
959
+ } catch (error) {
960
+ console.error('❌ OntoWave initialization failed:', error);
961
+ this.showError('Erreur d\'initialisation OntoWave: ' + error.message);
962
+ }
963
+ }
964
+
965
+ async loadConfigFromScript() {
966
+ // Priorité 1: Chercher dans window.ontoWaveConfig
967
+ if (window.ontoWaveConfig) {
968
+ this.config = { ...this.config, ...window.ontoWaveConfig };
969
+ console.log('📄 Configuration loaded from window.ontoWaveConfig');
970
+ console.log('📄 Final config:', this.config);
971
+ return;
972
+ }
973
+
974
+ // Priorité 2: Chercher dans script tag
975
+ const configScript = document.getElementById('ontowave-config');
976
+ if (configScript && configScript.type === 'application/json') {
977
+ try {
978
+ const userConfig = JSON.parse(configScript.textContent);
979
+ this.config = { ...this.config, ...userConfig };
980
+ console.log('📄 Configuration loaded from script tag');
981
+ console.log('📄 Final config:', this.config);
982
+ } catch (error) {
983
+ console.warn('⚠️ Invalid JSON in ontowave-config script tag:', error);
984
+ }
985
+ }
986
+ }
987
+
988
+ injectStyles() {
989
+ const styleElement = document.createElement('style');
990
+ styleElement.textContent = CSS_STYLES;
991
+ document.head.appendChild(styleElement);
992
+ }
993
+
994
+ async loadMermaid() {
995
+ return new Promise((resolve) => {
996
+ if (window.mermaid) {
997
+ this.mermaidLoaded = true;
998
+ this.initializeMermaid();
999
+ resolve();
1000
+ return;
1001
+ }
1002
+
1003
+ const script = document.createElement('script');
1004
+ script.src = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js';
1005
+ script.onload = () => {
1006
+ this.mermaidLoaded = true;
1007
+ this.initializeMermaid();
1008
+ resolve();
1009
+ };
1010
+ script.onerror = () => {
1011
+ console.warn('⚠️ Failed to load Mermaid library');
1012
+ resolve();
1013
+ };
1014
+ document.head.appendChild(script);
1015
+ });
1016
+ }
1017
+
1018
+ initializeMermaid() {
1019
+ if (window.mermaid) {
1020
+ window.mermaid.initialize(this.config.mermaid);
1021
+ console.log('🎨 Mermaid initialized');
1022
+ }
1023
+ }
1024
+
1025
+ async loadPrism() {
1026
+ return new Promise((resolve) => {
1027
+ if (window.Prism) {
1028
+ this.prismLoaded = true;
1029
+ console.log('🎨 Prism already loaded');
1030
+ return resolve();
1031
+ }
1032
+
1033
+ // Charger CSS Prism
1034
+ const cssLink = document.createElement('link');
1035
+ cssLink.rel = 'stylesheet';
1036
+ cssLink.href = 'https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css';
1037
+ document.head.appendChild(cssLink);
1038
+
1039
+ // Charger JS Prism
1040
+ const script = document.createElement('script');
1041
+ script.src = 'https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-core.min.js';
1042
+ script.onload = () => {
1043
+ console.log('🎨 Prism core loaded');
1044
+
1045
+ // Charger les langages essentiels et attendre leur chargement
1046
+ const essentialLanguages = ['markup', 'css', 'javascript'];
1047
+ let loadedCount = 0;
1048
+
1049
+ const checkComplete = () => {
1050
+ loadedCount++;
1051
+ console.log(`🔤 Essential language loaded: ${loadedCount}/${essentialLanguages.length}`);
1052
+ if (loadedCount >= essentialLanguages.length) {
1053
+ // HTML est un alias de markup dans Prism
1054
+ if (window.Prism.languages.markup) {
1055
+ window.Prism.languages.html = window.Prism.languages.markup;
1056
+ console.log('🔤 HTML alias created from markup');
1057
+ }
1058
+
1059
+ this.prismLoaded = true;
1060
+ console.log('✅ Prism ready with essential languages');
1061
+ resolve();
1062
+
1063
+ // Charger les langages supplémentaires en arrière-plan
1064
+ const additionalLanguages = ['python', 'java', 'bash', 'json', 'yaml', 'typescript'];
1065
+ additionalLanguages.forEach(lang => {
1066
+ const langScript = document.createElement('script');
1067
+ langScript.src = `https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-${lang}.min.js`;
1068
+ langScript.onload = () => {
1069
+ console.log(`🔤 Additional Prism language loaded: ${lang}`);
1070
+ };
1071
+ langScript.onerror = () => {
1072
+ console.warn(`⚠️ Failed to load Prism language: ${lang}`);
1073
+ };
1074
+ document.head.appendChild(langScript);
1075
+ });
1076
+ }
1077
+ };
1078
+
1079
+ essentialLanguages.forEach(lang => {
1080
+ const langScript = document.createElement('script');
1081
+ langScript.src = `https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-${lang}.min.js`;
1082
+ langScript.onload = checkComplete;
1083
+ langScript.onerror = () => {
1084
+ console.warn(`⚠️ Failed to load essential Prism language: ${lang}`);
1085
+ checkComplete(); // Continue même en cas d'erreur
1086
+ };
1087
+ document.head.appendChild(langScript);
1088
+ });
1089
+ };
1090
+ script.onerror = () => {
1091
+ console.warn('⚠️ Failed to load Prism library');
1092
+ resolve();
1093
+ };
1094
+ document.head.appendChild(script);
1095
+ });
1096
+ }
1097
+
1098
+ createInterface(locale = null) {
1099
+ // Trouver ou créer le conteneur
1100
+ this.container = document.getElementById(this.config.containerId);
1101
+ if (!this.container) {
1102
+ this.container = document.createElement('div');
1103
+ this.container.id = this.config.containerId;
1104
+ document.body.appendChild(this.container);
1105
+ }
1106
+
1107
+ this.container.className = 'ontowave-container';
1108
+
1109
+ // Créer les options du menu selon la configuration
1110
+ const galleryOption = this.config.showGallery ?
1111
+ `<span class="ontowave-menu-option" onclick="window.location.href='gallery.html'">🎨 ${this.t('menuGallery', locale)}</span>` : '';
1112
+
1113
+ // Créer les boutons de langue si multilingue et selon la configuration
1114
+ const languageButtonsMode = this.config.ui?.languageButtons || "menu";
1115
+ const shouldCreateMenuButtons = (languageButtonsMode === "menu" || languageButtonsMode === "both");
1116
+
1117
+ const languageButtons = this.config.locales && this.config.locales.length > 1 && shouldCreateMenuButtons ?
1118
+ this.config.locales.map(lang => {
1119
+ const isActive = (locale || this.getCurrentLanguage()) === lang;
1120
+ const activeClass = isActive ? ' active' : '';
1121
+ return `<span class="ontowave-menu-option ontowave-lang-btn${activeClass}" onclick="event.stopPropagation(); window.OntoWave.instance.switchLanguage('${lang}');">🌐 ${lang.toUpperCase()}</span>`;
1122
+ }).join('') : '';
1123
+
1124
+ // Créer la structure HTML minimaliste
1125
+ this.container.innerHTML = `
1126
+ <div class="ontowave-floating-menu" id="ontowave-floating-menu" title="OntoWave Menu">
1127
+ <span class="ontowave-menu-icon" id="ontowave-menu-icon">&#127754;</span>
1128
+ <div class="ontowave-menu-content" id="ontowave-menu-content">
1129
+ <a href="https://ontowave.org/" target="_blank" class="ontowave-menu-brand">OntoWave<span class="org-suffix">.org</span></a>
1130
+ <div class="ontowave-menu-options">
1131
+ <span class="ontowave-menu-option" onclick="window.OntoWave.instance.goHome()">🏠 ${this.t('menuHome', locale)}</span>
1132
+ ${galleryOption}
1133
+ ${languageButtons}
1134
+ <span class="ontowave-menu-option" onclick="event.stopPropagation(); window.OntoWave.instance.toggleConfigurationPanel(event, '${locale || this.getCurrentLanguage()}');">⚙️ ${this.t('menuConfiguration', locale)}</span>
1135
+ </div>
1136
+ </div>
1137
+ </div>
1138
+
1139
+ <div class="ontowave-content" id="ontowave-content">
1140
+ <div class="ontowave-loading">⏳ Chargement du contenu...</div>
1141
+ </div>
1142
+
1143
+ <div class="ontowave-status" style="display: none;">
1144
+ <h4>✅ OntoWave Package Actif</h4>
1145
+ <ul>
1146
+ <li><strong>Chargement rapide:</strong> Système intégré</li>
1147
+ <li><strong>Mermaid:</strong> ${this.mermaidLoaded ? 'Chargé' : 'Non disponible'}</li>
1148
+ <li><strong>Prism:</strong> ${this.prismLoaded ? 'Chargé' : 'Non disponible'}</li>
1149
+ <li><strong>PlantUML:</strong> Support intégré</li>
1150
+ <li><strong>Navigation:</strong> Hash préservé</li>
1151
+ </ul>
1152
+ </div>
1153
+ `;
1154
+
1155
+ // Créer les boutons de langue fixés si multilingue
1156
+ this.createFixedLanguageButtons(locale);
1157
+ }
1158
+
1159
+ createFixedLanguageButtons(locale = null) {
1160
+ // Supprimer les boutons existants s'ils existent
1161
+ const existingButtons = document.getElementById('ontowave-fixed-lang-buttons');
1162
+ if (existingButtons) {
1163
+ existingButtons.remove();
1164
+ }
1165
+
1166
+ // Créer les boutons de langue fixés seulement si multilingue et si configuré
1167
+ const languageButtonsMode = this.config.ui?.languageButtons || "menu";
1168
+ const shouldCreateFixed = (languageButtonsMode === "fixed" || languageButtonsMode === "both");
1169
+
1170
+ if (this.config.locales && this.config.locales.length > 1 && shouldCreateFixed) {
1171
+ const currentLang = locale || this.getCurrentLanguage();
1172
+
1173
+ const fixedLangContainer = document.createElement('div');
1174
+ fixedLangContainer.id = 'ontowave-fixed-lang-buttons';
1175
+ fixedLangContainer.className = 'ontowave-fixed-lang-buttons';
1176
+
1177
+ // Créer les boutons pour chaque langue
1178
+ const buttonsHtml = this.config.locales.map(lang => {
1179
+ const isActive = currentLang === lang;
1180
+ const activeClass = isActive ? ' active' : '';
1181
+ const flag = this.getLanguageFlag(lang);
1182
+ return `<button class="ontowave-fixed-lang-btn${activeClass}" onclick="window.OntoWave.instance.switchLanguage('${lang}')" title="Changer en ${lang.toUpperCase()}">${flag} ${lang.toUpperCase()}</button>`;
1183
+ }).join('');
1184
+
1185
+ fixedLangContainer.innerHTML = buttonsHtml;
1186
+ document.body.appendChild(fixedLangContainer);
1187
+
1188
+ console.log('🌐 Boutons de langue fixés créés:', this.config.locales, 'Mode:', languageButtonsMode);
1189
+ }
1190
+ }
1191
+
1192
+ getLanguageFlag(lang) {
1193
+ const flags = {
1194
+ 'fr': '🇫🇷',
1195
+ 'en': '🇬🇧',
1196
+ 'es': '🇪🇸',
1197
+ 'de': '🇩🇪',
1198
+ 'it': '🇮🇹',
1199
+ 'pt': '🇵🇹',
1200
+ 'zh': '🇨🇳',
1201
+ 'ja': '🇯🇵',
1202
+ 'ko': '🇰🇷',
1203
+ 'ru': '🇷🇺'
1204
+ };
1205
+ return flags[lang] || '🌐';
1206
+ }
1207
+
1208
+ initializeNavigation() {
1209
+ // Gestion des changements de hash
1210
+ window.addEventListener('hashchange', () => {
1211
+ const hash = location.hash.replace('#', '') || this.config.defaultPage;
1212
+ this.loadPage(hash);
1213
+ });
1214
+
1215
+ // Navigation par défaut
1216
+ this.createDefaultNavigation();
1217
+
1218
+ // Initialiser le menu flottant interactif
1219
+ this.initializeFloatingMenu();
1220
+ }
1221
+
1222
+ initializeFloatingMenu() {
1223
+ const menu = document.getElementById('ontowave-floating-menu');
1224
+ const menuIcon = document.getElementById('ontowave-menu-icon');
1225
+
1226
+ if (!menu || !menuIcon) return;
1227
+
1228
+ let isExpanded = false;
1229
+ let isDragging = false;
1230
+ let dragOffset = { x: 0, y: 0 };
1231
+
1232
+ // Fonction pour mettre à jour l'état de déplacement
1233
+ function updateDragState() {
1234
+ const canDrag = !isExpanded && !document.querySelector('.ontowave-config-panel');
1235
+ if (canDrag) {
1236
+ menu.classList.remove('no-drag');
1237
+ } else {
1238
+ menu.classList.add('no-drag');
1239
+ // Sécurité : forcer l'arrêt du drag
1240
+ isDragging = false;
1241
+ document.body.style.userSelect = '';
1242
+ document.body.style.cursor = '';
1243
+ }
1244
+ }
1245
+
1246
+ // Stocker la référence globalement pour les panneaux de configuration
1247
+ window.ontowaveUpdateDragState = updateDragState;
1248
+
1249
+ // Toggle menu au clic sur l'icône
1250
+ menuIcon.addEventListener('click', (e) => {
1251
+ e.stopPropagation();
1252
+ isExpanded = !isExpanded;
1253
+
1254
+ if (isExpanded) {
1255
+ menu.classList.add('expanded');
1256
+ } else {
1257
+ menu.classList.remove('expanded');
1258
+ }
1259
+ updateDragState();
1260
+ });
1261
+
1262
+ // Fermer le menu au clic en dehors
1263
+ document.addEventListener('click', (e) => {
1264
+ if (!menu.contains(e.target) && isExpanded) {
1265
+ isExpanded = false;
1266
+ menu.classList.remove('expanded');
1267
+ updateDragState();
1268
+ }
1269
+ });
1270
+
1271
+ // Drag & Drop functionality
1272
+ menu.addEventListener('mousedown', (e) => {
1273
+ // Ne pas démarrer le drag si le menu est étendu ou si un panneau de config est ouvert
1274
+ if (isExpanded || document.querySelector('.ontowave-config-panel')) {
1275
+ return;
1276
+ }
1277
+
1278
+ // Ne pas démarrer le drag si on clique sur les liens/boutons
1279
+ if (e.target.closest('a, .ontowave-menu-option')) return;
1280
+
1281
+ isDragging = true;
1282
+ const rect = menu.getBoundingClientRect();
1283
+ dragOffset.x = e.clientX - rect.left;
1284
+ dragOffset.y = e.clientY - rect.top;
1285
+
1286
+ menu.style.cursor = 'grabbing';
1287
+ document.body.style.userSelect = 'none';
1288
+
1289
+ // Empêcher l'interception des événements par d'autres éléments
1290
+ e.preventDefault();
1291
+ e.stopPropagation();
1292
+ });
1293
+
1294
+ document.addEventListener('mousemove', (e) => {
1295
+ if (!isDragging) return;
1296
+
1297
+ const x = e.clientX - dragOffset.x;
1298
+ const y = e.clientY - dragOffset.y;
1299
+
1300
+ // Contraintes pour rester dans la fenêtre
1301
+ const maxX = window.innerWidth - menu.offsetWidth;
1302
+ const maxY = window.innerHeight - menu.offsetHeight;
1303
+
1304
+ menu.style.left = Math.max(0, Math.min(maxX, x)) + 'px';
1305
+ menu.style.top = Math.max(0, Math.min(maxY, y)) + 'px';
1306
+ });
1307
+
1308
+ document.addEventListener('mouseup', () => {
1309
+ if (isDragging) {
1310
+ isDragging = false;
1311
+ menu.style.cursor = 'move';
1312
+ document.body.style.userSelect = '';
1313
+ }
1314
+ });
1315
+
1316
+ // Solution de sécurité : Remettre l'état normal après un délai
1317
+ function resetDragState() {
1318
+ isDragging = false;
1319
+ menu.style.cursor = 'move';
1320
+ document.body.style.userSelect = '';
1321
+ document.body.style.cursor = '';
1322
+ }
1323
+
1324
+ // Reset automatique après perte de focus ou changement de page
1325
+ document.addEventListener('visibilitychange', resetDragState);
1326
+ window.addEventListener('blur', resetDragState);
1327
+ window.addEventListener('focus', resetDragState);
1328
+
1329
+ // Fonction globale accessible pour reset manuel
1330
+ window.resetOntoWaveDragState = resetDragState;
1331
+
1332
+ // Support tactile pour mobile
1333
+ menu.addEventListener('touchstart', (e) => {
1334
+ if (e.target.closest('a, .ontowave-menu-option')) return;
1335
+
1336
+ const touch = e.touches[0];
1337
+ const rect = menu.getBoundingClientRect();
1338
+ dragOffset.x = touch.clientX - rect.left;
1339
+ dragOffset.y = touch.clientY - rect.top;
1340
+ isDragging = true;
1341
+ });
1342
+
1343
+ document.addEventListener('touchmove', (e) => {
1344
+ if (!isDragging) return;
1345
+ e.preventDefault();
1346
+
1347
+ const touch = e.touches[0];
1348
+ const x = touch.clientX - dragOffset.x;
1349
+ const y = touch.clientY - dragOffset.y;
1350
+
1351
+ const maxX = window.innerWidth - menu.offsetWidth;
1352
+ const maxY = window.innerHeight - menu.offsetHeight;
1353
+
1354
+ menu.style.left = Math.max(0, Math.min(maxX, x)) + 'px';
1355
+ menu.style.top = Math.max(0, Math.min(maxY, y)) + 'px';
1356
+ });
1357
+
1358
+ document.addEventListener('touchend', () => {
1359
+ isDragging = false;
1360
+ });
1361
+
1362
+ // Initialiser l'état de déplacement
1363
+ updateDragState();
1364
+
1365
+ // Améliorer la gestion des clics sur les options du menu
1366
+ this.enhanceMenuOptionClicks();
1367
+ }
1368
+
1369
+ enhanceMenuOptionClicks() {
1370
+ // Ajouter des gestionnaires d'événements robustes pour les options du menu
1371
+ const configOption = document.querySelector('.ontowave-menu-option[onclick*="toggleConfigurationPanel"]');
1372
+ if (configOption) {
1373
+ configOption.addEventListener('click', (e) => {
1374
+ e.preventDefault();
1375
+ e.stopPropagation();
1376
+ console.log('Configuration button clicked via event listener');
1377
+ this.toggleConfigurationPanel(e);
1378
+ }, { capture: true });
1379
+ }
1380
+ }
1381
+
1382
+ createDefaultNavigation() {
1383
+ const navGrid = document.getElementById('ontowave-nav-grid');
1384
+ if (!navGrid) return;
1385
+
1386
+ const defaultNavItems = [
1387
+ { href: 'index.md', icon: '🏠', label: 'Accueil' },
1388
+ { href: 'en/index.md', icon: '🇬🇧', label: 'English' },
1389
+ { href: 'fr/index.md', icon: '🇫🇷', label: 'Français' },
1390
+ { href: 'demo/mermaid.md', icon: '🧜‍♀️', label: 'Démo Mermaid' },
1391
+ { href: 'demo/plantuml.md', icon: '🏭', label: 'PlantUML' },
1392
+ { href: 'demo/advanced-shapes.md', icon: '🎯', label: 'Formes Avancées' }
1393
+ ];
1394
+
1395
+ navGrid.innerHTML = defaultNavItems.map(item => `
1396
+ <a href="#${item.href}" class="ontowave-nav-item" onclick="window.OntoWave.loadPage('${item.href}')">
1397
+ ${item.icon} ${item.label}
1398
+ </a>
1399
+ `).join('');
1400
+ }
1401
+
1402
+ async loadInitialPage() {
1403
+ const currentHash = location.hash.replace('#', '');
1404
+
1405
+ // Mode multilingue : redirection automatique si pas de hash
1406
+ if (this.isMultilingualMode() && !currentHash) {
1407
+ const defaultSource = this.config.sources[this.config.defaultLocale];
1408
+ console.log('🌐 Multilingual mode detected');
1409
+ console.log('🌐 Default locale:', this.config.defaultLocale);
1410
+ console.log('🌐 Default source:', defaultSource);
1411
+ console.log('🌐 Sources config:', this.config.sources);
1412
+
1413
+ if (defaultSource) {
1414
+ console.log('🌐 Multilingual mode: redirecting to', defaultSource);
1415
+ location.hash = '#' + defaultSource;
1416
+ return;
1417
+ }
1418
+ }
1419
+
1420
+ const initialPage = currentHash || this.config.defaultPage;
1421
+
1422
+ // Si on n'a pas de fichier index, afficher la configuration
1423
+ if (initialPage === 'index.md') {
1424
+ const candidates = this.generatePageCandidates(initialPage);
1425
+ let found = false;
1426
+
1427
+ for (const candidate of candidates) {
1428
+ try {
1429
+ const response = await fetch(this.config.baseUrl + candidate, { method: 'HEAD' });
1430
+ if (response.ok) {
1431
+ await this.loadPage(candidate);
1432
+ found = true;
1433
+ break;
1434
+ }
1435
+ } catch (error) {
1436
+ // Continue avec le candidat suivant
1437
+ continue;
1438
+ }
1439
+ }
1440
+
1441
+ if (!found) {
1442
+ console.log('📄 No index file found, showing configuration');
1443
+ this.showConfigurationInterface();
1444
+ return;
1445
+ }
1446
+ } else {
1447
+ await this.loadPage(initialPage);
1448
+ }
1449
+ }
1450
+
1451
+ async loadPage(pagePath) {
1452
+ const contentDiv = document.getElementById('ontowave-content');
1453
+ if (!contentDiv) return;
1454
+
1455
+ console.log('📄 Loading page:', pagePath);
1456
+ this.currentPage = pagePath;
1457
+
1458
+ // Sécurité : Reset de l'état de drag au changement de page
1459
+ if (window.resetOntoWaveDragState) {
1460
+ window.resetOntoWaveDragState();
1461
+ }
1462
+
1463
+ // Mettre à jour le hash
1464
+ if (location.hash !== '#' + pagePath) {
1465
+ location.hash = '#' + pagePath;
1466
+ }
1467
+
1468
+ // Mettre à jour le breadcrumb
1469
+ this.updateBreadcrumb(pagePath);
1470
+
1471
+ // Afficher le loading
1472
+ contentDiv.innerHTML = '<div class="ontowave-loading">⏳ Chargement de ' + pagePath + '...</div>';
1473
+
1474
+ try {
1475
+ const response = await fetch(this.config.baseUrl + pagePath);
1476
+
1477
+ if (!response.ok) {
1478
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1479
+ }
1480
+
1481
+ const markdown = await response.text();
1482
+ console.log('✅ Content loaded:', markdown.length, 'characters');
1483
+
1484
+ // Rendre le markdown
1485
+ const html = await this.renderMarkdown(markdown);
1486
+ contentDiv.innerHTML = html;
1487
+
1488
+ // Traiter les diagrammes
1489
+ await this.processDiagrams(contentDiv);
1490
+
1491
+ // Traiter la coloration syntaxique
1492
+ await this.processPrism(contentDiv);
1493
+
1494
+ } catch (error) {
1495
+ console.error('❌ Failed to load page:', error);
1496
+ this.showError(`Impossible de charger ${pagePath}: ${error.message}`);
1497
+ }
1498
+ }
1499
+
1500
+ updateBreadcrumb(pagePath) {
1501
+ const breadcrumbDiv = document.getElementById('ontowave-breadcrumb');
1502
+ if (!breadcrumbDiv || !this.config.navigation.showBreadcrumb) return;
1503
+
1504
+ const parts = pagePath.split('/');
1505
+ const breadcrumbs = ['<a href="#' + this.config.defaultPage + '">🏠 Accueil</a>'];
1506
+
1507
+ let currentPath = '';
1508
+ parts.forEach((part, index) => {
1509
+ if (index === parts.length - 1) {
1510
+ // Dernier élément (page actuelle)
1511
+ breadcrumbs.push('<span>' + part.replace('.md', '') + '</span>');
1512
+ } else {
1513
+ currentPath += (currentPath ? '/' : '') + part;
1514
+ breadcrumbs.push('<a href="#' + currentPath + '/index.md">' + part + '</a>');
1515
+ }
1516
+ });
1517
+
1518
+ breadcrumbDiv.innerHTML = breadcrumbs.join(' <span>›</span> ');
1519
+ breadcrumbDiv.style.display = 'block';
1520
+ }
1521
+
1522
+ async renderMarkdown(markdown) {
1523
+ // Rendu markdown corrigé avec support complet
1524
+ let html = markdown;
1525
+
1526
+ // Traiter les blocs de code AVANT les autres transformations
1527
+ const codeBlocks = [];
1528
+ html = html.replace(/```(\w+)([\s\S]*?)```/g, (match, language, content) => {
1529
+ const trimmedContent = content.trim();
1530
+ const placeholder = `__CODE_BLOCK_${codeBlocks.length}__`;
1531
+
1532
+ if (language === 'mermaid') {
1533
+ const id = 'mermaid-' + Math.random().toString(36).substr(2, 9);
1534
+ codeBlocks.push(`<div class="ontowave-mermaid">
1535
+ <div style="margin-bottom: 8px; font-weight: bold; color: #586069;">🧜‍♀️ Diagramme Mermaid</div>
1536
+ <div class="mermaid" id="${id}">${trimmedContent}</div>
1537
+ </div>`);
1538
+ } else if (language === 'plantuml') {
1539
+ const id = 'plantuml-' + Math.random().toString(36).substr(2, 9);
1540
+
1541
+ // Fonction d'encodage PlantUML avec support UTF-8
1542
+ function encodePlantUML(text) {
1543
+ // Encoder le texte en UTF-8 puis en hexadécimal
1544
+ const utf8Encoder = new TextEncoder();
1545
+ const utf8Bytes = utf8Encoder.encode(text);
1546
+ let hex = '';
1547
+ for (let i = 0; i < utf8Bytes.length; i++) {
1548
+ hex += utf8Bytes[i].toString(16).padStart(2, '0');
1549
+ }
1550
+ return 'h' + hex; // Le préfixe ~h sera ajouté dans l'URL
1551
+ }
1552
+
1553
+ const encodedContent = encodePlantUML(trimmedContent);
1554
+ const plantUMLUrl = `${this.config.plantuml.server}/${this.config.plantuml.format}/~${encodedContent}`;
1555
+ codeBlocks.push(`<div class="ontowave-plantuml" id="${id}">
1556
+ <div style="margin-bottom: 8px; font-weight: bold; color: #586069;">🏭 Diagramme PlantUML</div>
1557
+ <img src="${plantUMLUrl}" alt="Diagramme PlantUML" style="max-width: 100%; height: auto;"
1558
+ onerror="this.parentElement.innerHTML='<div style=\\'color: #d73a49; padding: 20px;\\'>❌ Erreur de rendu PlantUML</div>'" />
1559
+ </div>`);
1560
+ } else {
1561
+ const codeClass = this.prismLoaded ? `language-${language}` : '';
1562
+ console.log(`📝 Processing code block: language="${language}", prismLoaded=${this.prismLoaded}, class="${codeClass}"`);
1563
+
1564
+ // Échapper le HTML pour que Prism puisse le colorer correctement
1565
+ const escapedContent = trimmedContent
1566
+ .replace(/&/g, '&amp;')
1567
+ .replace(/</g, '&lt;')
1568
+ .replace(/>/g, '&gt;')
1569
+ .replace(/"/g, '&quot;')
1570
+ .replace(/'/g, '&#39;');
1571
+
1572
+ codeBlocks.push(`<pre class="ontowave-code"><code class="${codeClass}">${escapedContent}</code></pre>`);
1573
+ }
1574
+
1575
+ return placeholder;
1576
+ });
1577
+
1578
+ // Transformations markdown principales
1579
+ html = html
1580
+ // Headers - corriger l'ordre et la syntaxe (du plus spécifique au plus général)
1581
+ .replace(/^###### (.+)$/gm, '<h6>$1</h6>')
1582
+ .replace(/^##### (.+)$/gm, '<h5>$1</h5>')
1583
+ .replace(/^#### (.+)$/gm, '<h4>$1</h4>')
1584
+ .replace(/^### (.+)$/gm, '<h3>$1</h3>')
1585
+ .replace(/^## (.+)$/gm, '<h2>$1</h2>')
1586
+ .replace(/^# (.+)$/gm, '<h1>$1</h1>')
1587
+ // Séparateurs HR
1588
+ .replace(/^---+$/gm, '<hr>')
1589
+ // Images markdown - avant les liens
1590
+ .replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1">')
1591
+ // Liens - traitement différent pour HTML vs MD
1592
+ .replace(/\[([^\]]+)\]\(([^)]+\.html[^)]*)\)/g, '<a href="$2">$1</a>')
1593
+ // Liens externes (http/https) - ne pas ajouter de hash
1594
+ .replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, '<a href="$2" target="_blank">$1</a>')
1595
+ // Fichiers téléchargeables - liens directs sans hash
1596
+ .replace(/\[([^\]]+)\]\(([^)]+\.(tar\.gz|zip|pdf|doc|docx|xls|xlsx|ppt|pptx|txt|csv|json|xml|js|css|png|jpg|jpeg|gif|svg|webp)[^)]*)\)/g, '<a href="$2" download>$1</a>')
1597
+ // Liens internes - ajouter hash et navigation OntoWave
1598
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="#$2" onclick="window.OntoWave.loadPage(\'$2\')">$1</a>')
1599
+ // Formatage
1600
+ .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
1601
+ .replace(/\*(.+?)\*/g, '<em>$1</em>')
1602
+ // Code inline
1603
+ .replace(/`([^`]+)`/g, '<code>$1</code>')
1604
+ // Paragraphes
1605
+ .split('\n\n')
1606
+ .map(para => para.trim())
1607
+ .filter(para => para.length > 0)
1608
+ .map(para => {
1609
+ // Ne pas encapsuler dans <p> si c'est déjà un élément HTML
1610
+ if (para.match(/^<(h[1-6]|hr|div|pre)/)) {
1611
+ return para;
1612
+ }
1613
+ return `<p>${para.replace(/\n/g, '<br>')}</p>`;
1614
+ })
1615
+ .join('\n');
1616
+
1617
+ // Remettre les blocs de code
1618
+ codeBlocks.forEach((block, index) => {
1619
+ html = html.replace(`__CODE_BLOCK_${index}__`, block);
1620
+ });
1621
+
1622
+ return html;
1623
+ }
1624
+
1625
+ async processDiagrams(container) {
1626
+ if (!this.mermaidLoaded || !window.mermaid) return;
1627
+
1628
+ const mermaidElements = container.querySelectorAll('.mermaid');
1629
+ if (mermaidElements.length === 0) return;
1630
+
1631
+ console.log('🎨 Processing', mermaidElements.length, 'Mermaid diagrams');
1632
+
1633
+ try {
1634
+ // Nettoyer les éléments déjà traités
1635
+ mermaidElements.forEach(el => {
1636
+ el.removeAttribute('data-processed');
1637
+ });
1638
+
1639
+ await window.mermaid.run();
1640
+ console.log('✅ Mermaid diagrams rendered successfully');
1641
+
1642
+ // Vérification post-rendu
1643
+ setTimeout(() => {
1644
+ const svgElements = container.querySelectorAll('.mermaid svg');
1645
+ console.log('🎨 SVG elements found:', svgElements.length);
1646
+
1647
+ if (svgElements.length === 0 && mermaidElements.length > 0) {
1648
+ console.log('⚠️ Retrying Mermaid rendering...');
1649
+ mermaidElements.forEach(el => {
1650
+ el.removeAttribute('data-processed');
1651
+ });
1652
+ window.mermaid.init(undefined, mermaidElements);
1653
+ }
1654
+ }, 1000);
1655
+
1656
+ } catch (error) {
1657
+ console.error('❌ Mermaid rendering error:', error);
1658
+ // Fallback: afficher le code brut
1659
+ mermaidElements.forEach(el => {
1660
+ if (!el.querySelector('svg')) {
1661
+ el.innerHTML = `<div style="color: #d73a49; padding: 10px;">❌ Erreur de rendu Mermaid: ${error.message}</div><pre style="background: #f8f8f8; padding: 10px; margin-top: 10px; border-radius: 4px;"><code>${el.textContent}</code></pre>`;
1662
+ }
1663
+ });
1664
+ }
1665
+ }
1666
+
1667
+ async processPrism(container) {
1668
+ console.log('🔍 processPrism called - prismLoaded:', this.prismLoaded, 'window.Prism:', !!window.Prism);
1669
+
1670
+ if (!window.Prism) {
1671
+ console.log('🎨 Prism not available, skipping syntax highlighting');
1672
+ return;
1673
+ }
1674
+
1675
+ try {
1676
+ // Vérifier les langages disponibles
1677
+ console.log('🔤 Available Prism languages:', window.Prism.languages ? Object.keys(window.Prism.languages) : 'none');
1678
+
1679
+ // Trouver tous les blocs de code avec des classes de langue
1680
+ const codeElements = container.querySelectorAll('code[class*="language-"]');
1681
+ console.log('🎨 Found', codeElements.length, 'code blocks with language classes');
1682
+
1683
+ // Debug détaillé de chaque bloc de code
1684
+ codeElements.forEach((el, i) => {
1685
+ console.log(`🔍 Code block ${i}:`);
1686
+ console.log(` - class: "${el.className}"`);
1687
+ console.log(` - content length: ${el.textContent?.length}`);
1688
+ console.log(` - content preview: "${el.textContent?.substring(0, 50)}..."`);
1689
+ console.log(` - parent visible: ${window.getComputedStyle(el.parentElement).display !== 'none'}`);
1690
+ console.log(` - element visible: ${window.getComputedStyle(el).display !== 'none'}`);
1691
+ });
1692
+
1693
+ // Aussi chercher les blocs sans classe pour debug
1694
+ const allCodeElements = container.querySelectorAll('code');
1695
+ console.log('📝 Total code blocks found:', allCodeElements.length);
1696
+
1697
+ if (codeElements.length > 0) {
1698
+ // Tenter manuellement sur le premier élément pour debug
1699
+ const firstElement = codeElements[0];
1700
+ console.log('🧪 Testing manual highlighting on first element...');
1701
+
1702
+ // Vérifier le langage
1703
+ const classList = firstElement.className.split(' ');
1704
+ const langClass = classList.find(cls => cls.startsWith('language-'));
1705
+ const lang = langClass ? langClass.replace('language-', '') : 'unknown';
1706
+ console.log(`🔤 Language detected: "${lang}"`);
1707
+ console.log(`🔤 Language available in Prism: ${!!(window.Prism.languages && window.Prism.languages[lang])}`);
1708
+
1709
+ // Test manuel
1710
+ if (window.Prism.languages && window.Prism.languages[lang]) {
1711
+ console.log('🧪 Attempting manual highlight...');
1712
+ const originalContent = firstElement.textContent;
1713
+ try {
1714
+ const highlighted = window.Prism.highlight(originalContent, window.Prism.languages[lang], lang);
1715
+ console.log(`🎨 Manual highlight result length: ${highlighted.length}`);
1716
+ console.log(`🎨 Manual highlight preview: "${highlighted.substring(0, 100)}..."`);
1717
+
1718
+ // Appliquer le résultat
1719
+ firstElement.innerHTML = highlighted;
1720
+ console.log('✅ Manual highlight applied');
1721
+ } catch (manualError) {
1722
+ console.error('❌ Manual highlight failed:', manualError);
1723
+ }
1724
+ }
1725
+
1726
+ // Puis essayer la méthode normale
1727
+ window.Prism.highlightAllUnder(container);
1728
+ console.log('✅ Prism syntax highlighting applied to', codeElements.length, 'blocks');
1729
+
1730
+ // Vérifier que la coloration a fonctionné
1731
+ const tokenElements = container.querySelectorAll('.token');
1732
+ console.log('🎨 Tokens created after highlighting:', tokenElements.length);
1733
+
1734
+ // Debug des tokens créés
1735
+ if (tokenElements.length > 0) {
1736
+ tokenElements.forEach((token, i) => {
1737
+ console.log(`Token ${i}: "${token.textContent}" (class: ${token.className})`);
1738
+ });
1739
+ }
1740
+ } else {
1741
+ console.log('⚠️ No code blocks with language classes found for Prism');
1742
+ }
1743
+ } catch (error) {
1744
+ console.error('❌ Prism highlighting error:', error);
1745
+ }
1746
+ }
1747
+
1748
+ showConfigurationInterface() {
1749
+ const contentDiv = document.getElementById('ontowave-content');
1750
+ if (!contentDiv) return;
1751
+
1752
+ const currentConfigString = JSON.stringify(this.config, null, 2)
1753
+ .replace(/"/g, '&quot;')
1754
+ .replace(/</g, '&lt;')
1755
+ .replace(/>/g, '&gt;');
1756
+
1757
+ contentDiv.innerHTML = `
1758
+ <div class="ontowave-config-interface">
1759
+ <div class="config-header">
1760
+ <h1>🌊 OntoWave Configuration</h1>
1761
+ <p>Aucun fichier index trouvé. Configurez OntoWave pour votre projet :</p>
1762
+ </div>
1763
+
1764
+ <div class="config-content">
1765
+ <div class="config-form">
1766
+ <h2>📝 Configuration</h2>
1767
+
1768
+ <div class="form-group">
1769
+ <label for="config-title">Titre du site :</label>
1770
+ <input type="text" id="config-title" />
1771
+ </div>
1772
+
1773
+ <div class="form-group">
1774
+ <label for="config-defaultPage">Page par défaut :</label>
1775
+ <input type="text" id="config-defaultPage" />
1776
+ </div>
1777
+
1778
+ <div class="form-group">
1779
+ <label for="config-locales">Langues supportées (séparées par des virgules) :</label>
1780
+ <input type="text" id="config-locales" placeholder="fr-CA, fr, en" />
1781
+ </div>
1782
+
1783
+ <div class="form-group">
1784
+ <label>
1785
+ <input type="checkbox" id="config-showGallery" />
1786
+ Afficher la galerie d'exemples
1787
+ </label>
1788
+ </div>
1789
+
1790
+ <div class="form-group">
1791
+ <label for="config-mermaidTheme">Thème Mermaid :</label>
1792
+ <select id="config-mermaidTheme">
1793
+ <option value="default">Default</option>
1794
+ <option value="dark">Dark</option>
1795
+ <option value="forest">Forest</option>
1796
+ <option value="neutral">Neutral</option>
1797
+ </select>
1798
+ </div>
1799
+
1800
+ <div class="form-actions">
1801
+ <button onclick="window.OntoWave.instance.updateConfigFromForm()">✅ Appliquer</button>
1802
+ <button onclick="window.OntoWave.instance.downloadConfig()">💾 Télécharger HTML</button>
1803
+ <button onclick="window.OntoWave.instance.resetConfig()">🔄 Reset</button>
1804
+ </div>
1805
+ </div>
1806
+
1807
+ <div class="config-code">
1808
+ <h2>💻 Code HTML généré</h2>
1809
+ <div class="code-preview">
1810
+ <pre><code id="generated-html">&lt;!DOCTYPE html&gt;
1811
+ &lt;html&gt;
1812
+ &lt;head&gt;
1813
+ &lt;meta charset="UTF-8"&gt;
1814
+ &lt;title&gt;${this.config.title}&lt;/title&gt;
1815
+ &lt;/head&gt;
1816
+ &lt;body&gt;
1817
+ &lt;script src="ontowave.min.js"&gt;&lt;/script&gt;
1818
+ &lt;script type="application/json" id="ontowave-config"&gt;
1819
+ ${currentConfigString}
1820
+ &lt;/script&gt;
1821
+ &lt;/body&gt;
1822
+ &lt;/html&gt;</code></pre>
1823
+ </div>
1824
+
1825
+ <div class="usage-info">
1826
+ <h3>📋 Instructions d'utilisation</h3>
1827
+ <ol>
1828
+ <li>Configurez les options dans le formulaire</li>
1829
+ <li>Cliquez sur "Télécharger HTML" pour obtenir votre fichier</li>
1830
+ <li>Placez vos fichiers .md dans le même dossier</li>
1831
+ <li>Ouvrez le fichier HTML dans votre navigateur</li>
1832
+ </ol>
1833
+
1834
+ <h3>🌐 Gestion des langues</h3>
1835
+ <ul>
1836
+ <li><strong>Monolingue :</strong> Laissez "Langues supportées" vide</li>
1837
+ <li><strong>Multilingue :</strong> Ajoutez les codes de langue (ex: fr, en, fr-CA)</li>
1838
+ <li><strong>Fichiers :</strong> index.fr.md, index.en.md, etc.</li>
1839
+ <li><strong>Fallback :</strong> index.md si aucune langue trouvée</li>
1840
+ </ul>
1841
+ </div>
1842
+ </div>
1843
+ </div>
1844
+ </div>
1845
+ `;
1846
+
1847
+ // Ajouter les styles pour l'interface de configuration
1848
+ this.addConfigStyles();
1849
+
1850
+ // Remplir les valeurs des champs après génération du HTML (une seule fois)
1851
+ this.populateConfigForm();
1852
+
1853
+ // Générer le code HTML initial
1854
+ this.updateGeneratedCode();
1855
+ }
1856
+
1857
+ // Méthode pour remplir les valeurs du formulaire
1858
+ populateConfigForm() {
1859
+ const titleField = document.getElementById('config-title');
1860
+ const defaultPageField = document.getElementById('config-defaultPage');
1861
+ const localesField = document.getElementById('config-locales');
1862
+ const showGalleryField = document.getElementById('config-showGallery');
1863
+ const mermaidThemeField = document.getElementById('config-mermaidTheme');
1864
+
1865
+ if (titleField) titleField.value = this.config.title;
1866
+ if (defaultPageField) defaultPageField.value = this.config.defaultPage;
1867
+ if (localesField) localesField.value = this.config.locales.join(', ');
1868
+ if (showGalleryField) showGalleryField.checked = this.config.showGallery;
1869
+ if (mermaidThemeField) mermaidThemeField.value = this.config.mermaid.theme;
1870
+ }
1871
+
1872
+ addConfigStyles() {
1873
+ if (document.getElementById('ontowave-config-styles')) return;
1874
+
1875
+ const style = document.createElement('style');
1876
+ style.id = 'ontowave-config-styles';
1877
+ style.textContent = `
1878
+ .ontowave-config-interface {
1879
+ max-width: 1200px;
1880
+ margin: 0 auto;
1881
+ padding: 20px;
1882
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
1883
+ }
1884
+
1885
+ .config-header {
1886
+ text-align: center;
1887
+ margin-bottom: 40px;
1888
+ }
1889
+
1890
+ .config-header h1 {
1891
+ color: #0969da;
1892
+ margin-bottom: 10px;
1893
+ }
1894
+
1895
+ .config-content {
1896
+ display: grid;
1897
+ grid-template-columns: 1fr 1fr;
1898
+ gap: 40px;
1899
+ align-items: start;
1900
+ }
1901
+
1902
+ .config-form {
1903
+ background: #f8f9fa;
1904
+ padding: 30px;
1905
+ border-radius: 12px;
1906
+ border: 1px solid #e1e4e8;
1907
+ }
1908
+
1909
+ .config-code {
1910
+ background: #ffffff;
1911
+ padding: 30px;
1912
+ border-radius: 12px;
1913
+ border: 1px solid #e1e4e8;
1914
+ }
1915
+
1916
+ .form-group {
1917
+ margin-bottom: 20px;
1918
+ }
1919
+
1920
+ .form-group label {
1921
+ display: block;
1922
+ font-weight: 600;
1923
+ margin-bottom: 8px;
1924
+ color: #24292e;
1925
+ }
1926
+
1927
+ .form-group input, .form-group select {
1928
+ width: 100%;
1929
+ padding: 10px;
1930
+ border: 1px solid #d0d7de;
1931
+ border-radius: 6px;
1932
+ font-size: 14px;
1933
+ }
1934
+
1935
+ .form-group input[type="checkbox"] {
1936
+ width: auto;
1937
+ margin-right: 8px;
1938
+ }
1939
+
1940
+ .form-actions {
1941
+ display: flex;
1942
+ gap: 10px;
1943
+ margin-top: 30px;
1944
+ }
1945
+
1946
+ .form-actions button {
1947
+ flex: 1;
1948
+ padding: 12px 20px;
1949
+ border: none;
1950
+ border-radius: 6px;
1951
+ font-weight: 600;
1952
+ cursor: pointer;
1953
+ transition: all 0.2s ease;
1954
+ }
1955
+
1956
+ .form-actions button:first-child {
1957
+ background: #28a745;
1958
+ color: white;
1959
+ }
1960
+
1961
+ .form-actions button:nth-child(2) {
1962
+ background: #0969da;
1963
+ color: white;
1964
+ }
1965
+
1966
+ .form-actions button:last-child {
1967
+ background: #6c757d;
1968
+ color: white;
1969
+ }
1970
+
1971
+ .form-actions button:hover {
1972
+ transform: translateY(-1px);
1973
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
1974
+ }
1975
+
1976
+ .code-preview {
1977
+ background: #f6f8fa;
1978
+ border: 1px solid #e1e4e8;
1979
+ border-radius: 6px;
1980
+ padding: 16px;
1981
+ overflow-x: auto;
1982
+ margin-bottom: 20px;
1983
+ }
1984
+
1985
+ .code-preview pre {
1986
+ margin: 0;
1987
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
1988
+ font-size: 13px;
1989
+ line-height: 1.4;
1990
+ }
1991
+
1992
+ .usage-info h3 {
1993
+ color: #0969da;
1994
+ margin-top: 25px;
1995
+ margin-bottom: 10px;
1996
+ }
1997
+
1998
+ .usage-info ul, .usage-info ol {
1999
+ padding-left: 20px;
2000
+ }
2001
+
2002
+ .usage-info li {
2003
+ margin-bottom: 5px;
2004
+ }
2005
+
2006
+ @media (max-width: 768px) {
2007
+ .config-content {
2008
+ grid-template-columns: 1fr;
2009
+ gap: 20px;
2010
+ }
2011
+ }
2012
+ `;
2013
+
2014
+ document.head.appendChild(style);
2015
+ }
2016
+
2017
+ updateConfigFromForm() {
2018
+ // Mettre à jour la configuration depuis le formulaire
2019
+ const title = document.getElementById('config-title').value;
2020
+ const defaultPage = document.getElementById('config-defaultPage').value;
2021
+ const locales = document.getElementById('config-locales').value
2022
+ .split(',')
2023
+ .map(l => l.trim())
2024
+ .filter(l => l.length > 0);
2025
+ const showGallery = document.getElementById('config-showGallery').checked;
2026
+ const mermaidTheme = document.getElementById('config-mermaidTheme').value;
2027
+
2028
+ this.config.title = title;
2029
+ this.config.defaultPage = defaultPage;
2030
+ this.config.locales = locales;
2031
+ this.config.showGallery = showGallery;
2032
+ this.config.mermaid.theme = mermaidTheme;
2033
+
2034
+ console.log('📝 Configuration updated:', this.config);
2035
+
2036
+ // Mettre à jour le titre de la page
2037
+ document.title = this.config.title;
2038
+
2039
+ // Régénérer le code HTML
2040
+ this.updateGeneratedCode();
2041
+
2042
+ // Afficher notification
2043
+ this.showNotification('✅ Configuration mise à jour');
2044
+ }
2045
+
2046
+ updateGeneratedCode() {
2047
+ // Créer une config simplifiée pour l'affichage
2048
+ const simpleConfig = {
2049
+ title: this.config.title,
2050
+ baseUrl: this.config.baseUrl,
2051
+ defaultPage: this.config.defaultPage,
2052
+ locales: this.config.locales,
2053
+ fallbackLocale: this.config.fallbackLocale,
2054
+ showGallery: this.config.showGallery,
2055
+ mermaid: {
2056
+ theme: this.config.mermaid.theme
2057
+ }
2058
+ };
2059
+
2060
+ const configString = JSON.stringify(simpleConfig, null, 2)
2061
+ .replace(/"/g, '&quot;')
2062
+ .replace(/</g, '&lt;')
2063
+ .replace(/>/g, '&gt;');
2064
+
2065
+ const htmlCode = `&lt;!DOCTYPE html&gt;
2066
+ &lt;html&gt;
2067
+ &lt;head&gt;
2068
+ &lt;meta charset="UTF-8"&gt;
2069
+ &lt;title&gt;${this.config.title}&lt;/title&gt;
2070
+ &lt;/head&gt;
2071
+ &lt;body&gt;
2072
+ &lt;script src="ontowave.min.js"&gt;&lt;/script&gt;
2073
+ &lt;script type="application/json" id="ontowave-config"&gt;
2074
+ ${configString}
2075
+ &lt;/script&gt;
2076
+ &lt;/body&gt;
2077
+ &lt;/html&gt;`;
2078
+
2079
+ const codeElement = document.getElementById('generated-html');
2080
+ if (codeElement) {
2081
+ codeElement.innerHTML = htmlCode;
2082
+ }
2083
+ }
2084
+
2085
+ downloadConfig() {
2086
+ // Utiliser la config simplifiée pour le téléchargement aussi
2087
+ const simpleConfig = {
2088
+ title: this.config.title,
2089
+ baseUrl: this.config.baseUrl,
2090
+ defaultPage: this.config.defaultPage,
2091
+ locales: this.config.locales,
2092
+ fallbackLocale: this.config.fallbackLocale,
2093
+ showGallery: this.config.showGallery,
2094
+ mermaid: {
2095
+ theme: this.config.mermaid.theme
2096
+ }
2097
+ };
2098
+
2099
+ const configString = JSON.stringify(simpleConfig, null, 2);
2100
+
2101
+ const htmlContent = `<!DOCTYPE html>
2102
+ <html>
2103
+ <head>
2104
+ <meta charset="UTF-8">
2105
+ <title>${this.config.title}</title>
2106
+ </head>
2107
+ <body>
2108
+ <script src="ontowave.min.js"></script>
2109
+ <script type="application/json" id="ontowave-config">
2110
+ ${configString}
2111
+ </script>
2112
+ </body>
2113
+ </html>`;
2114
+
2115
+ const blob = new Blob([htmlContent], { type: 'text/html' });
2116
+ const url = URL.createObjectURL(blob);
2117
+
2118
+ const a = document.createElement('a');
2119
+ a.href = url;
2120
+ a.download = 'index.html';
2121
+ document.body.appendChild(a);
2122
+ a.click();
2123
+ document.body.removeChild(a);
2124
+ URL.revokeObjectURL(url);
2125
+
2126
+ this.showNotification('💾 Fichier HTML téléchargé');
2127
+ }
2128
+
2129
+ resetConfig() {
2130
+ this.config = { ...DEFAULT_CONFIG };
2131
+ this.showConfigurationInterface();
2132
+ this.showNotification('🔄 Configuration réinitialisée');
2133
+ }
2134
+
2135
+ showNotification(message) {
2136
+ // Créer une notification temporaire
2137
+ const notification = document.createElement('div');
2138
+ notification.style.cssText = `
2139
+ position: fixed;
2140
+ top: 20px;
2141
+ right: 20px;
2142
+ background: #28a745;
2143
+ color: white;
2144
+ padding: 12px 20px;
2145
+ border-radius: 6px;
2146
+ z-index: 10000;
2147
+ font-weight: 600;
2148
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
2149
+ animation: slideIn 0.3s ease;
2150
+ `;
2151
+ notification.textContent = message;
2152
+
2153
+ // Ajouter animation CSS
2154
+ if (!document.getElementById('notification-styles')) {
2155
+ const style = document.createElement('style');
2156
+ style.id = 'notification-styles';
2157
+ style.textContent = `
2158
+ @keyframes slideIn {
2159
+ from { transform: translateX(100%); opacity: 0; }
2160
+ to { transform: translateX(0); opacity: 1; }
2161
+ }
2162
+ `;
2163
+ document.head.appendChild(style);
2164
+ }
2165
+
2166
+ document.body.appendChild(notification);
2167
+
2168
+ setTimeout(() => {
2169
+ notification.style.animation = 'slideIn 0.3s ease reverse';
2170
+ setTimeout(() => {
2171
+ if (notification.parentNode) {
2172
+ notification.parentNode.removeChild(notification);
2173
+ }
2174
+ }, 300);
2175
+ }, 3000);
2176
+ }
2177
+
2178
+ showError(message) {
2179
+ const contentDiv = document.getElementById('ontowave-content');
2180
+ if (contentDiv) {
2181
+ contentDiv.innerHTML = `<div class="ontowave-error">❌ ${message}</div>`;
2182
+ }
2183
+ }
2184
+
2185
+ // Panneau de configuration dans le menu flottant
2186
+ toggleConfigurationPanel(event, locale = null) {
2187
+ if (event) {
2188
+ event.preventDefault();
2189
+ event.stopPropagation();
2190
+ }
2191
+
2192
+ const targetLang = locale || this.getCurrentLanguage();
2193
+ console.log('⚙️ Opening config panel with locale:', targetLang);
2194
+
2195
+ const menuContent = document.querySelector('.ontowave-menu-content');
2196
+ if (!menuContent) {
2197
+ console.error('Menu content not found');
2198
+ return;
2199
+ }
2200
+
2201
+ // Trouver le bouton Configuration
2202
+ const configButton = document.querySelector('.ontowave-menu-option[onclick*="toggleConfigurationPanel"]');
2203
+
2204
+ // Vérifier si le panneau existe déjà
2205
+ let configPanel = document.getElementById('ontowave-config-panel');
2206
+
2207
+ if (configPanel) {
2208
+ // Si le panneau existe, le supprimer (toggle off)
2209
+ configPanel.remove();
2210
+ if (configButton) {
2211
+ configButton.classList.remove('selected');
2212
+ }
2213
+
2214
+ // Supprimer la classe pour réactiver le zoom au survol
2215
+ const floatingMenu = document.getElementById('ontowave-floating-menu');
2216
+ if (floatingMenu) {
2217
+ floatingMenu.classList.remove('has-config-panel');
2218
+ }
2219
+
2220
+ // Mettre à jour l'état de déplacement après fermeture du panneau
2221
+ if (typeof window.ontowaveUpdateDragState === 'function') {
2222
+ window.ontowaveUpdateDragState();
2223
+ }
2224
+ console.log('Config panel closed');
2225
+ return;
2226
+ }
2227
+
2228
+ // Marquer le bouton comme sélectionné
2229
+ if (configButton) {
2230
+ configButton.classList.add('selected');
2231
+ }
2232
+
2233
+ // Créer le panneau de configuration
2234
+ configPanel = document.createElement('div');
2235
+ configPanel.id = 'ontowave-config-panel';
2236
+ configPanel.className = 'ontowave-config-panel';
2237
+
2238
+ configPanel.innerHTML = `
2239
+ <div class="config-panel-content">
2240
+ <div class="config-full-panel">
2241
+ <h3>🌊 ${this.t('configTitle', targetLang)}</h3>
2242
+
2243
+ <!-- Section Général -->
2244
+ <div class="config-section">
2245
+ <h4>📖 ${this.t('configGeneral', targetLang)}</h4>
2246
+ <div class="config-row">
2247
+ <div class="form-group-full">
2248
+ <label for="config-title-full">${this.t('configSiteTitle', targetLang)}</label>
2249
+ <input type="text" id="config-title-full" value="${this.config.title}" />
2250
+ </div>
2251
+ <div class="form-group-full">
2252
+ <label for="config-defaultPage-full">${this.t('configDefaultPage', targetLang)}</label>
2253
+ <input type="text" id="config-defaultPage-full" value="${this.config.defaultPage}" placeholder="index.md" />
2254
+ </div>
2255
+ </div>
2256
+ <div class="form-group-full">
2257
+ <label for="config-baseUrl-full">${this.t('configBaseUrl', targetLang)}</label>
2258
+ <input type="text" id="config-baseUrl-full" value="${this.config.baseUrl}" placeholder="/" />
2259
+ </div>
2260
+ </div>
2261
+
2262
+ <!-- Section Langues et Localisation -->
2263
+ <div class="config-section">
2264
+ <h4>🌍 ${this.t('configLanguages', targetLang)}</h4>
2265
+ <div class="config-row">
2266
+ <div class="form-group-full">
2267
+ <label for="config-locales-full">${this.t('configSupportedLanguages', targetLang)}</label>
2268
+ <input type="text" id="config-locales-full" value="${this.config.locales.join(', ')}" placeholder="fr-CA, fr, en" />
2269
+ <small>${this.t('configLanguageNote', targetLang)}</small>
2270
+ </div>
2271
+ <div class="form-group-full">
2272
+ <label for="config-fallbackLocale-full">${this.t('configFallbackLanguage', targetLang)}</label>
2273
+ <select id="config-fallbackLocale-full">
2274
+ <option value="en" ${this.config.fallbackLocale === 'en' ? 'selected' : ''}>English (en)</option>
2275
+ <option value="fr" ${this.config.fallbackLocale === 'fr' ? 'selected' : ''}>Français (fr)</option>
2276
+ <option value="es" ${this.config.fallbackLocale === 'es' ? 'selected' : ''}>Español (es)</option>
2277
+ <option value="de" ${this.config.fallbackLocale === 'de' ? 'selected' : ''}>Deutsch (de)</option>
2278
+ </select>
2279
+ </div>
2280
+ </div>
2281
+ </div>
2282
+
2283
+ <!-- Section Navigation et Interface -->
2284
+ <div class="config-section">
2285
+ <h4>🧭 ${this.t('configNavigation', targetLang)}</h4>
2286
+ <div class="config-row">
2287
+ <div class="form-group-checkbox">
2288
+ <label>
2289
+ <input type="checkbox" id="config-showGallery-full" ${this.config.showGallery ? 'checked' : ''} />
2290
+ 🎨 ${this.t('configShowGallery', targetLang)}
2291
+ </label>
2292
+ </div>
2293
+ <div class="form-group-checkbox">
2294
+ <label>
2295
+ <input type="checkbox" id="config-navHome-full" ${this.config.navigation?.showHome !== false ? 'checked' : ''} />
2296
+ 🏠 ${this.t('configHomeButton', targetLang)}
2297
+ </label>
2298
+ </div>
2299
+ </div>
2300
+ <div class="config-row">
2301
+ <div class="form-group-checkbox">
2302
+ <label>
2303
+ <input type="checkbox" id="config-navBreadcrumb-full" ${this.config.navigation?.showBreadcrumb !== false ? 'checked' : ''} />
2304
+ 📍 ${this.t('configBreadcrumb', targetLang)}
2305
+ </label>
2306
+ </div>
2307
+ <div class="form-group-checkbox">
2308
+ <label>
2309
+ <input type="checkbox" id="config-navToc-full" ${this.config.navigation?.showToc !== false ? 'checked' : ''} />
2310
+ 📑 ${this.t('configToc', targetLang)}
2311
+ </label>
2312
+ </div>
2313
+ </div>
2314
+ </div>
2315
+
2316
+ <!-- Section Diagrammes Mermaid -->
2317
+ <div class="config-section">
2318
+ <h4>📊 ${this.t('configMermaid', targetLang)}</h4>
2319
+ <div class="config-row">
2320
+ <div class="form-group-full">
2321
+ <label for="config-mermaidTheme-full">${this.t('configMermaidTheme', targetLang)}</label>
2322
+ <select id="config-mermaidTheme-full">
2323
+ <option value="default" ${this.config.mermaid?.theme === 'default' ? 'selected' : ''}>Default (clair)</option>
2324
+ <option value="dark" ${this.config.mermaid?.theme === 'dark' ? 'selected' : ''}>Dark (sombre)</option>
2325
+ <option value="forest" ${this.config.mermaid?.theme === 'forest' ? 'selected' : ''}>Forest (vert)</option>
2326
+ <option value="neutral" ${this.config.mermaid?.theme === 'neutral' ? 'selected' : ''}>Neutral (neutre)</option>
2327
+ </select>
2328
+ </div>
2329
+ <div class="form-group-checkbox">
2330
+ <label>
2331
+ <input type="checkbox" id="config-mermaidStart-full" ${this.config.mermaid?.startOnLoad !== false ? 'checked' : ''} />
2332
+ 🚀 ${this.t('configMermaidAuto', targetLang)}
2333
+ </label>
2334
+ </div>
2335
+ </div>
2336
+ <div class="config-row">
2337
+ <div class="form-group-checkbox">
2338
+ <label>
2339
+ <input type="checkbox" id="config-mermaidMaxWidth-full" ${this.config.mermaid?.flowchart?.useMaxWidth !== false ? 'checked' : ''} />
2340
+ 📐 ${this.t('configMermaidMaxWidth', targetLang)}
2341
+ </label>
2342
+ </div>
2343
+ </div>
2344
+ </div>
2345
+
2346
+ <!-- Section PlantUML -->
2347
+ <div class="config-section">
2348
+ <h4>🌿 ${this.t('configPlantuml', targetLang)}</h4>
2349
+ <div class="config-row">
2350
+ <div class="form-group-full">
2351
+ <label for="config-plantumlServer-full">${this.t('configPlantumlServer', targetLang)}</label>
2352
+ <input type="text" id="config-plantumlServer-full" value="${this.config.plantuml?.server || 'https://www.plantuml.com/plantuml'}" />
2353
+ </div>
2354
+ <div class="form-group-full">
2355
+ <label for="config-plantumlFormat-full">${this.t('configPlantumlFormat', targetLang)}</label>
2356
+ <select id="config-plantumlFormat-full">
2357
+ <option value="svg" ${this.config.plantuml?.format === 'svg' ? 'selected' : ''}>SVG (vectoriel)</option>
2358
+ <option value="png" ${this.config.plantuml?.format === 'png' ? 'selected' : ''}>PNG (bitmap)</option>
2359
+ </select>
2360
+ </div>
2361
+ </div>
2362
+ </div>
2363
+
2364
+ <!-- Section Coloration Syntaxique -->
2365
+ <div class="config-section">
2366
+ <h4>🎨 ${this.t('configPrism', targetLang)}</h4>
2367
+ <div class="config-row">
2368
+ <div class="form-group-full">
2369
+ <label for="config-prismTheme-full">${this.t('configPrismTheme', targetLang)}</label>
2370
+ <select id="config-prismTheme-full">
2371
+ <option value="default" ${this.config.prism?.theme === 'default' ? 'selected' : ''}>Default (clair)</option>
2372
+ <option value="dark" ${this.config.prism?.theme === 'dark' ? 'selected' : ''}>Dark (sombre)</option>
2373
+ <option value="twilight" ${this.config.prism?.theme === 'twilight' ? 'selected' : ''}>Twilight</option>
2374
+ </select>
2375
+ </div>
2376
+ <div class="form-group-checkbox">
2377
+ <label>
2378
+ <input type="checkbox" id="config-prismAutoload-full" ${this.config.prism?.autoload !== false ? 'checked' : ''} />
2379
+ 🔄 ${this.t('configPrismAutoload', targetLang)}
2380
+ </label>
2381
+ </div>
2382
+ </div>
2383
+ </div>
2384
+
2385
+ <!-- Section Interface Utilisateur -->
2386
+ <div class="config-section">
2387
+ <h4>💻 ${this.t('configUI', targetLang)}</h4>
2388
+ <div class="config-row">
2389
+ <div class="form-group-full">
2390
+ <label for="config-uiTheme-full">${this.t('configUITheme', targetLang)}</label>
2391
+ <select id="config-uiTheme-full">
2392
+ <option value="default" ${this.config.ui?.theme === 'default' ? 'selected' : ''}>Default (clair)</option>
2393
+ <option value="dark" ${this.config.ui?.theme === 'dark' ? 'selected' : ''}>Dark (sombre)</option>
2394
+ <option value="auto" ${this.config.ui?.theme === 'auto' ? 'selected' : ''}>Auto (système)</option>
2395
+ </select>
2396
+ </div>
2397
+ <div class="form-group-checkbox">
2398
+ <label>
2399
+ <input type="checkbox" id="config-uiResponsive-full" ${this.config.ui?.responsive !== false ? 'checked' : ''} />
2400
+ 📱 ${this.t('configUIResponsive', targetLang)}
2401
+ </label>
2402
+ </div>
2403
+ </div>
2404
+ <div class="config-row">
2405
+ <div class="form-group-checkbox">
2406
+ <label>
2407
+ <input type="checkbox" id="config-uiAnimations-full" ${this.config.ui?.animations !== false ? 'checked' : ''} />
2408
+ ✨ ${this.t('configUIAnimations', targetLang)}
2409
+ </label>
2410
+ </div>
2411
+ </div>
2412
+ </div>
2413
+
2414
+ <!-- Actions -->
2415
+ <div class="form-actions-full">
2416
+ <button onclick="window.OntoWave.instance.updateConfigFromFullPanel()" class="btn-primary">✅ ${this.t('configApply', targetLang)}</button>
2417
+ <button onclick="window.OntoWave.instance.downloadConfigFromPanel()" class="btn-secondary">💾 ${this.t('configDownloadHTML', targetLang)}</button>
2418
+ <button onclick="window.OntoWave.instance.downloadOntoWaveScript()" class="btn-secondary">📥 ${this.t('configDownloadJS', targetLang)}</button>
2419
+ <button onclick="window.OntoWave.instance.resetConfigToDefaults()" class="btn-warning">🔄 ${this.t('configReset', targetLang)}</button>
2420
+ </div>
2421
+ </div>
2422
+ </div>
2423
+ `;
2424
+
2425
+ // Ajouter les styles du panneau
2426
+ this.addConfigPanelStyles();
2427
+
2428
+ // Insérer le panneau après le menu
2429
+ menuContent.appendChild(configPanel);
2430
+
2431
+ // Ajouter la classe pour désactiver le zoom au survol
2432
+ const floatingMenu = document.getElementById('ontowave-floating-menu');
2433
+ if (floatingMenu) {
2434
+ floatingMenu.classList.add('has-config-panel');
2435
+ }
2436
+
2437
+ // Mettre à jour l'état de déplacement après ouverture du panneau
2438
+ if (typeof window.ontowaveUpdateDragState === 'function') {
2439
+ window.ontowaveUpdateDragState();
2440
+ }
2441
+
2442
+ // Générer le code HTML initial
2443
+ this.updateGeneratedCodeMini();
2444
+
2445
+ console.log('Config panel opened');
2446
+ }
2447
+
2448
+ // Méthodes pour le panneau complet
2449
+ updateConfigFromFullPanel() {
2450
+ // Général
2451
+ const title = document.getElementById('config-title-full')?.value || this.config.title;
2452
+ const defaultPage = document.getElementById('config-defaultPage-full')?.value || this.config.defaultPage;
2453
+ const baseUrl = document.getElementById('config-baseUrl-full')?.value || this.config.baseUrl;
2454
+
2455
+ // Langues
2456
+ const locales = document.getElementById('config-locales-full')?.value
2457
+ .split(',')
2458
+ .map(l => l.trim())
2459
+ .filter(l => l.length > 0) || this.config.locales;
2460
+ const fallbackLocale = document.getElementById('config-fallbackLocale-full')?.value || this.config.fallbackLocale;
2461
+
2462
+ // Navigation
2463
+ const showGallery = document.getElementById('config-showGallery-full')?.checked || false;
2464
+ const showHome = document.getElementById('config-navHome-full')?.checked !== false;
2465
+ const showBreadcrumb = document.getElementById('config-navBreadcrumb-full')?.checked !== false;
2466
+ const showToc = document.getElementById('config-navToc-full')?.checked !== false;
2467
+
2468
+ // Mermaid
2469
+ const mermaidTheme = document.getElementById('config-mermaidTheme-full')?.value || 'default';
2470
+ const mermaidStart = document.getElementById('config-mermaidStart-full')?.checked !== false;
2471
+ const mermaidMaxWidth = document.getElementById('config-mermaidMaxWidth-full')?.checked !== false;
2472
+
2473
+ // PlantUML
2474
+ const plantumlServer = document.getElementById('config-plantumlServer-full')?.value || 'https://www.plantuml.com/plantuml';
2475
+ const plantumlFormat = document.getElementById('config-plantumlFormat-full')?.value || 'svg';
2476
+
2477
+ // Prism
2478
+ const prismTheme = document.getElementById('config-prismTheme-full')?.value || 'default';
2479
+ const prismAutoload = document.getElementById('config-prismAutoload-full')?.checked !== false;
2480
+
2481
+ // UI
2482
+ const uiTheme = document.getElementById('config-uiTheme-full')?.value || 'default';
2483
+ const uiResponsive = document.getElementById('config-uiResponsive-full')?.checked !== false;
2484
+ const uiAnimations = document.getElementById('config-uiAnimations-full')?.checked !== false;
2485
+
2486
+ // Mettre à jour la configuration
2487
+ this.config.title = title;
2488
+ this.config.defaultPage = defaultPage;
2489
+ this.config.baseUrl = baseUrl;
2490
+ this.config.locales = locales;
2491
+ this.config.fallbackLocale = fallbackLocale;
2492
+ this.config.showGallery = showGallery;
2493
+
2494
+ this.config.navigation = {
2495
+ showHome: showHome,
2496
+ showBreadcrumb: showBreadcrumb,
2497
+ showToc: showToc
2498
+ };
2499
+
2500
+ this.config.mermaid = {
2501
+ theme: mermaidTheme,
2502
+ startOnLoad: mermaidStart,
2503
+ flowchart: { useMaxWidth: mermaidMaxWidth },
2504
+ sequence: { useMaxWidth: mermaidMaxWidth },
2505
+ gantt: { useMaxWidth: mermaidMaxWidth },
2506
+ journey: { useMaxWidth: mermaidMaxWidth }
2507
+ };
2508
+
2509
+ this.config.plantuml = {
2510
+ server: plantumlServer,
2511
+ format: plantumlFormat
2512
+ };
2513
+
2514
+ this.config.prism = {
2515
+ theme: prismTheme,
2516
+ autoload: prismAutoload
2517
+ };
2518
+
2519
+ this.config.ui = {
2520
+ theme: uiTheme,
2521
+ responsive: uiResponsive,
2522
+ animations: uiAnimations
2523
+ };
2524
+
2525
+ // Mettre à jour le titre de la page
2526
+ document.title = this.config.title;
2527
+
2528
+ // Afficher une notification
2529
+ this.showNotification('Configuration appliquée avec succès ! 🎉');
2530
+
2531
+ console.log('Configuration mise à jour:', this.config);
2532
+ }
2533
+
2534
+ resetConfigToDefaults() {
2535
+ if (confirm('Voulez-vous vraiment réinitialiser toute la configuration aux valeurs par défaut ?')) {
2536
+ // Réinitialiser avec les valeurs par défaut
2537
+ Object.assign(this.config, {
2538
+ title: "OntoWave Documentation",
2539
+ baseUrl: "/",
2540
+ defaultPage: "index.md",
2541
+ locales: [],
2542
+ fallbackLocale: "en",
2543
+ showGallery: false,
2544
+ mermaid: {
2545
+ theme: "default",
2546
+ startOnLoad: true,
2547
+ flowchart: { useMaxWidth: true },
2548
+ sequence: { useMaxWidth: true },
2549
+ gantt: { useMaxWidth: true },
2550
+ journey: { useMaxWidth: true }
2551
+ },
2552
+ plantuml: {
2553
+ server: "https://www.plantuml.com/plantuml",
2554
+ format: "svg"
2555
+ },
2556
+ prism: {
2557
+ theme: "default",
2558
+ autoload: true
2559
+ },
2560
+ navigation: {
2561
+ showHome: true,
2562
+ showBreadcrumb: true,
2563
+ showToc: true
2564
+ },
2565
+ ui: {
2566
+ theme: "default",
2567
+ responsive: true,
2568
+ animations: true
2569
+ }
2570
+ });
2571
+
2572
+ // Fermer et rouvrir le panneau pour actualiser les valeurs
2573
+ const configPanel = document.getElementById('ontowave-config-panel');
2574
+ if (configPanel) {
2575
+ configPanel.remove();
2576
+ // Mettre à jour l'état de déplacement après suppression du panneau
2577
+ if (typeof window.ontowaveUpdateDragState === 'function') {
2578
+ window.ontowaveUpdateDragState();
2579
+ }
2580
+ setTimeout(() => this.toggleConfigurationPanel(), 100);
2581
+ }
2582
+
2583
+ this.showNotification('Configuration réinitialisée ! 🔄');
2584
+ }
2585
+ }
2586
+
2587
+ // Méthodes pour le panneau compact (compatibilité)
2588
+ updateConfigFromPanel() {
2589
+ const title = document.getElementById('config-title-mini')?.value || this.config.title;
2590
+ const locales = document.getElementById('config-locales-mini')?.value
2591
+ .split(',')
2592
+ .map(l => l.trim())
2593
+ .filter(l => l.length > 0) || this.config.locales;
2594
+ const showGallery = document.getElementById('config-showGallery-mini')?.checked || this.config.showGallery;
2595
+
2596
+ this.config.title = title;
2597
+ this.config.locales = locales;
2598
+ this.config.showGallery = showGallery;
2599
+
2600
+ // Mettre à jour le titre de la page
2601
+ document.title = this.config.title;
2602
+
2603
+ // Régénérer le code HTML
2604
+ this.updateGeneratedCodeMini();
2605
+
2606
+ this.showNotification('✅ Configuration mise à jour');
2607
+ }
2608
+
2609
+ downloadConfigFromPanel() {
2610
+ // Utiliser la config simplifiée pour le téléchargement
2611
+ const simpleConfig = {
2612
+ title: this.config.title,
2613
+ baseUrl: this.config.baseUrl,
2614
+ defaultPage: this.config.defaultPage,
2615
+ locales: this.config.locales,
2616
+ fallbackLocale: this.config.fallbackLocale,
2617
+ showGallery: this.config.showGallery,
2618
+ mermaid: {
2619
+ theme: this.config.mermaid.theme
2620
+ }
2621
+ };
2622
+
2623
+ const configString = JSON.stringify(simpleConfig, null, 2);
2624
+
2625
+ const htmlContent = `<!DOCTYPE html>
2626
+ <html>
2627
+ <head>
2628
+ <meta charset="UTF-8">
2629
+ <title>${this.config.title}</title>
2630
+ </head>
2631
+ <body>
2632
+ <script src="ontowave.min.js"></script>
2633
+ <script type="application/json" id="ontowave-config">
2634
+ ${configString}
2635
+ </script>
2636
+ </body>
2637
+ </html>`;
2638
+
2639
+ const blob = new Blob([htmlContent], { type: 'text/html' });
2640
+ const url = URL.createObjectURL(blob);
2641
+
2642
+ const a = document.createElement('a');
2643
+ a.href = url;
2644
+ a.download = 'index.html';
2645
+ document.body.appendChild(a);
2646
+ a.click();
2647
+ document.body.removeChild(a);
2648
+ URL.revokeObjectURL(url);
2649
+
2650
+ this.showNotification('💾 Fichier HTML téléchargé');
2651
+ }
2652
+
2653
+ downloadOntoWaveScript() {
2654
+ // Créer un lien de téléchargement vers le fichier ontowave.min.js
2655
+ const a = document.createElement('a');
2656
+ a.href = 'ontowave.min.js';
2657
+ a.download = 'ontowave.min.js';
2658
+ document.body.appendChild(a);
2659
+ a.click();
2660
+ document.body.removeChild(a);
2661
+
2662
+ this.showNotification('📥 Fichier ontowave.min.js téléchargé');
2663
+ }
2664
+
2665
+ updateGeneratedCodeMini() {
2666
+ // Créer une config simplifiée pour l'affichage
2667
+ const simpleConfig = {
2668
+ title: this.config.title,
2669
+ baseUrl: this.config.baseUrl,
2670
+ defaultPage: this.config.defaultPage,
2671
+ locales: this.config.locales,
2672
+ fallbackLocale: this.config.fallbackLocale,
2673
+ showGallery: this.config.showGallery,
2674
+ mermaid: {
2675
+ theme: this.config.mermaid.theme
2676
+ }
2677
+ };
2678
+
2679
+ const configString = JSON.stringify(simpleConfig, null, 2)
2680
+ .replace(/"/g, '&quot;')
2681
+ .replace(/</g, '&lt;')
2682
+ .replace(/>/g, '&gt;');
2683
+
2684
+ const htmlCode = `&lt;!DOCTYPE html&gt;
2685
+ &lt;html&gt;
2686
+ &lt;head&gt;
2687
+ &lt;meta charset="UTF-8"&gt;
2688
+ &lt;title&gt;${this.config.title}&lt;/title&gt;
2689
+ &lt;/head&gt;
2690
+ &lt;body&gt;
2691
+ &lt;script src="ontowave.min.js"&gt;&lt;/script&gt;
2692
+ &lt;script type="application/json" id="ontowave-config"&gt;
2693
+ ${configString}
2694
+ &lt;/script&gt;
2695
+ &lt;/body&gt;
2696
+ &lt;/html&gt;`;
2697
+
2698
+ const codeElement = document.getElementById('generated-html-mini');
2699
+ if (codeElement) {
2700
+ codeElement.innerHTML = htmlCode;
2701
+ }
2702
+ }
2703
+
2704
+ addConfigPanelStyles() {
2705
+ if (document.getElementById('ontowave-config-panel-styles')) return;
2706
+
2707
+ const style = document.createElement('style');
2708
+ style.id = 'ontowave-config-panel-styles';
2709
+ style.textContent = `
2710
+ /* Panneau de configuration étendu */
2711
+ .ontowave-config-panel {
2712
+ position: absolute;
2713
+ top: 100%;
2714
+ left: 0;
2715
+ right: 0;
2716
+ background: white;
2717
+ border: 1px solid #e1e4e8;
2718
+ border-radius: 12px;
2719
+ box-shadow: 0 16px 48px rgba(0,0,0,0.15);
2720
+ z-index: 1001;
2721
+ margin-top: 12px;
2722
+ max-height: 90vh;
2723
+ overflow-y: auto;
2724
+ min-width: 90vw;
2725
+ max-width: 95vw;
2726
+ width: auto;
2727
+ }
2728
+
2729
+ .config-panel-content {
2730
+ padding: 0;
2731
+ }
2732
+
2733
+ .config-full-panel {
2734
+ padding: 32px;
2735
+ max-width: none;
2736
+ }
2737
+
2738
+ .config-full-panel h3 {
2739
+ margin: 0 0 32px 0;
2740
+ color: #0969da;
2741
+ font-size: 24px;
2742
+ font-weight: 700;
2743
+ text-align: center;
2744
+ padding-bottom: 16px;
2745
+ border-bottom: 2px solid #f6f8fa;
2746
+ }
2747
+
2748
+ /* Sections de configuration */
2749
+ .config-section {
2750
+ margin-bottom: 32px;
2751
+ padding: 24px;
2752
+ background: #f6f8fa;
2753
+ border-radius: 8px;
2754
+ border-left: 4px solid #0969da;
2755
+ }
2756
+
2757
+ .config-section h4 {
2758
+ margin: 0 0 20px 0;
2759
+ color: #24292e;
2760
+ font-size: 18px;
2761
+ font-weight: 600;
2762
+ display: flex;
2763
+ align-items: center;
2764
+ gap: 8px;
2765
+ }
2766
+
2767
+ /* Disposition en lignes */
2768
+ .config-row {
2769
+ display: grid;
2770
+ grid-template-columns: 1fr 1fr;
2771
+ gap: 20px;
2772
+ margin-bottom: 16px;
2773
+ }
2774
+
2775
+ .config-row:last-child {
2776
+ margin-bottom: 0;
2777
+ }
2778
+
2779
+ /* Groupes de formulaire */
2780
+ .form-group-full {
2781
+ margin-bottom: 0;
2782
+ }
2783
+
2784
+ .form-group-full label {
2785
+ display: block;
2786
+ font-weight: 600;
2787
+ margin-bottom: 8px;
2788
+ color: #24292e;
2789
+ font-size: 14px;
2790
+ }
2791
+
2792
+ .form-group-full input,
2793
+ .form-group-full select {
2794
+ width: 100%;
2795
+ padding: 12px;
2796
+ border: 2px solid #d0d7de;
2797
+ border-radius: 6px;
2798
+ font-size: 14px;
2799
+ transition: border-color 0.2s ease;
2800
+ }
2801
+
2802
+ .form-group-full input:focus,
2803
+ .form-group-full select:focus {
2804
+ outline: none;
2805
+ border-color: #0969da;
2806
+ box-shadow: 0 0 0 3px rgba(9, 105, 218, 0.1);
2807
+ }
2808
+
2809
+ .form-group-full small {
2810
+ display: block;
2811
+ margin-top: 4px;
2812
+ font-size: 12px;
2813
+ color: #656d76;
2814
+ font-style: italic;
2815
+ }
2816
+
2817
+ /* Checkboxes */
2818
+ .form-group-checkbox {
2819
+ display: flex;
2820
+ align-items: center;
2821
+ margin-bottom: 0;
2822
+ }
2823
+
2824
+ .form-group-checkbox label {
2825
+ display: flex;
2826
+ align-items: center;
2827
+ font-weight: 500;
2828
+ color: #24292e;
2829
+ font-size: 14px;
2830
+ cursor: pointer;
2831
+ margin: 0;
2832
+ }
2833
+
2834
+ .form-group-checkbox input[type="checkbox"] {
2835
+ width: auto;
2836
+ margin: 0 8px 0 0;
2837
+ transform: scale(1.2);
2838
+ accent-color: #0969da;
2839
+ }
2840
+
2841
+ /* Actions du formulaire */
2842
+ .form-actions-full {
2843
+ display: flex;
2844
+ gap: 16px;
2845
+ justify-content: center;
2846
+ margin-top: 40px;
2847
+ padding-top: 24px;
2848
+ border-top: 2px solid #f6f8fa;
2849
+ flex-wrap: wrap;
2850
+ }
2851
+
2852
+ .form-actions-full button {
2853
+ padding: 12px 24px;
2854
+ border: none;
2855
+ border-radius: 8px;
2856
+ font-size: 14px;
2857
+ font-weight: 600;
2858
+ cursor: pointer;
2859
+ transition: all 0.2s ease;
2860
+ min-width: 180px;
2861
+ display: flex;
2862
+ align-items: center;
2863
+ justify-content: center;
2864
+ gap: 8px;
2865
+ }
2866
+
2867
+ .btn-primary {
2868
+ background: #0969da;
2869
+ color: white;
2870
+ }
2871
+
2872
+ .btn-primary:hover {
2873
+ background: #0550ae;
2874
+ transform: translateY(-1px);
2875
+ box-shadow: 0 4px 12px rgba(9, 105, 218, 0.3);
2876
+ }
2877
+
2878
+ .btn-secondary {
2879
+ background: #6f7782;
2880
+ color: white;
2881
+ }
2882
+
2883
+ .btn-secondary:hover {
2884
+ background: #57606a;
2885
+ transform: translateY(-1px);
2886
+ box-shadow: 0 4px 12px rgba(111, 119, 130, 0.3);
2887
+ }
2888
+
2889
+ .btn-warning {
2890
+ background: #d73a49;
2891
+ color: white;
2892
+ }
2893
+
2894
+ .btn-warning:hover {
2895
+ background: #b31d28;
2896
+ transform: translateY(-1px);
2897
+ box-shadow: 0 4px 12px rgba(215, 58, 73, 0.3);
2898
+ }
2899
+
2900
+ /* Responsive pour petits écrans */
2901
+ @media (max-width: 768px) {
2902
+ .ontowave-config-panel {
2903
+ min-width: 95vw;
2904
+ margin-top: 8px;
2905
+ }
2906
+
2907
+ .config-full-panel {
2908
+ padding: 20px;
2909
+ }
2910
+
2911
+ .config-row {
2912
+ grid-template-columns: 1fr;
2913
+ gap: 16px;
2914
+ }
2915
+
2916
+ .form-actions-full {
2917
+ flex-direction: column;
2918
+ align-items: stretch;
2919
+ }
2920
+
2921
+ .form-actions-full button {
2922
+ min-width: auto;
2923
+ }
2924
+ }
2925
+
2926
+ /* Styles pour compatibilité avec l'ancien panneau compact */
2927
+ .config-form-compact h3,
2928
+ .config-preview-compact h3 {
2929
+ margin: 0 0 16px 0;
2930
+ color: #0969da;
2931
+ font-size: 16px;
2932
+ font-weight: 600;
2933
+ }
2934
+
2935
+ .form-group-compact {
2936
+ margin-bottom: 16px;
2937
+ }
2938
+
2939
+ .form-group-compact label {
2940
+ display: block;
2941
+ font-weight: 600;
2942
+ margin-bottom: 6px;
2943
+ color: #24292e;
2944
+ font-size: 13px;
2945
+ }
2946
+
2947
+ .form-group-compact input {
2948
+ width: 100%;
2949
+ padding: 8px;
2950
+ border: 1px solid #d0d7de;
2951
+ border-radius: 4px;
2952
+ font-size: 13px;
2953
+ }
2954
+
2955
+ .form-group-compact input[type="checkbox"] {
2956
+ width: auto;
2957
+ margin-right: 6px;
2958
+ }
2959
+
2960
+ .form-actions-compact {
2961
+ display: flex;
2962
+ gap: 10px;
2963
+ margin-top: 20px;
2964
+ flex-wrap: wrap;
2965
+ }
2966
+
2967
+ .form-actions-compact button {
2968
+ flex: 1;
2969
+ padding: 10px 16px;
2970
+ border: none;
2971
+ border-radius: 6px;
2972
+ background: #0969da;
2973
+ color: white;
2974
+ font-size: 13px;
2975
+ font-weight: 600;
2976
+ cursor: pointer;
2977
+ transition: background 0.2s ease;
2978
+ min-width: 120px;
2979
+ }
2980
+
2981
+ .form-actions-compact button:hover {
2982
+ background: #0550ae;
2983
+ }
2984
+
2985
+ .config-preview-compact {
2986
+ background: #f6f8fa;
2987
+ border-radius: 6px;
2988
+ padding: 16px;
2989
+ }
2990
+
2991
+ .code-preview-mini {
2992
+ background: #24292e;
2993
+ color: #f6f8fa;
2994
+ padding: 12px;
2995
+ border-radius: 4px;
2996
+ font-family: 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;
2997
+ font-size: 11px;
2998
+ line-height: 1.4;
2999
+ overflow-x: auto;
3000
+ max-height: 300px;
3001
+ overflow-y: auto;
3002
+ }
3003
+ `;
3004
+ document.head.appendChild(style);
3005
+ }
3006
+
3007
+ // API publique
3008
+ navigate(page) {
3009
+ this.loadPage(page);
3010
+ }
3011
+
3012
+ getConfig() {
3013
+ return { ...this.config };
3014
+ }
3015
+
3016
+ updateConfig(newConfig) {
3017
+ this.config = { ...this.config, ...newConfig };
3018
+ console.log('📝 Configuration updated');
3019
+ }
3020
+ }
3021
+
3022
+ // Fonction pour charger la configuration depuis config.json
3023
+ async function loadConfigFromFile() {
3024
+ try {
3025
+ const response = await fetch('./config.json');
3026
+ if (response.ok) {
3027
+ const config = await response.json();
3028
+ console.log('📁 Configuration chargée depuis config.json:', config);
3029
+ return config;
3030
+ }
3031
+ } catch (error) {
3032
+ console.log('📁 Pas de config.json trouvé, utilisation de la configuration par défaut');
3033
+ }
3034
+ return {};
3035
+ }
3036
+
3037
+ // Initialisation automatique au chargement de la page
3038
+ document.addEventListener('DOMContentLoaded', async () => {
3039
+ // Utiliser window.OntoWaveConfig si disponible, sinon charger depuis config.json
3040
+ let config = window.OntoWaveConfig || {};
3041
+ if (!window.OntoWaveConfig) {
3042
+ config = await loadConfigFromFile();
3043
+ } else {
3044
+ // Merger avec config.json si les deux existent
3045
+ const fileConfig = await loadConfigFromFile();
3046
+ config = { ...fileConfig, ...window.OntoWaveConfig };
3047
+ }
3048
+
3049
+ window.OntoWave = { instance: new OntoWave(config) };
3050
+ await window.OntoWave.instance.init();
3051
+ console.log('🌊 OntoWave initialisé automatiquement');
3052
+ });
3053
+
3054
+ // Export pour utilisation manuelle si nécessaire
3055
+ window.OntoWaveClass = OntoWave;
3056
+
3057
+ })(window);