Docfile commited on
Commit
41554ba
·
verified ·
1 Parent(s): 13f8f31

Create index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +577 -0
templates/index.html ADDED
@@ -0,0 +1,577 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Mariam M-0 | Solution Mathématique</title>
7
+ <!-- Tailwind CSS -->
8
+ <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
9
+
10
+ <!-- Configuration optimisée de MathJax pour expressions complexes -->
11
+ <script>
12
+ window.MathJax = {
13
+ tex: {
14
+ inlineMath: [['$', '$'], ['\\(', '\\)']],
15
+ displayMath: [['$$', '$$'], ['\\[', '\\]']],
16
+ processEscapes: true,
17
+ processEnvironments: true,
18
+ macros: {
19
+ R: '{\\mathbb{R}}',
20
+ N: '{\\mathbb{N}}',
21
+ Z: '{\\mathbb{Z}}',
22
+ C: '{\\mathbb{C}}',
23
+ Q: '{\\mathbb{Q}}'
24
+ },
25
+ packages: {
26
+ '[+]': [
27
+ 'autoload',
28
+ 'ams',
29
+ 'noerrors',
30
+ 'physics',
31
+ 'colorv2',
32
+ 'cancel',
33
+ 'mhchem'
34
+ ]
35
+ },
36
+ // Ignorer les erreurs
37
+ processClass: "math",
38
+ formatError: (jax, err) => {
39
+ console.warn('Erreur MathJax:', err);
40
+ return jax.formatError(err);
41
+ }
42
+ },
43
+ svg: {
44
+ fontCache: 'global',
45
+ scale: 1.2
46
+ },
47
+ options: {
48
+ enableMenu: true,
49
+ skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'code'],
50
+ renderActions: {
51
+ assistiveMml: []
52
+ }
53
+ },
54
+ loader: {
55
+ load: ['[tex]/physics', '[tex]/ams', '[tex]/colorv2', '[tex]/cancel', '[tex]/mhchem']
56
+ },
57
+ startup: {
58
+ pageReady: () => {
59
+ console.log('MathJax est complètement chargé.');
60
+ window.mathJaxReady = true;
61
+ document.dispatchEvent(new Event('mathjax-ready'));
62
+ }
63
+ }
64
+ };
65
+ </script>
66
+ <!-- Chargement du module SVG pour un meilleur rendu -->
67
+ <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js" id="MathJax-script" async></script>
68
+ <script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.min.js"></script>
69
+
70
+ <style>
71
+ @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;700&display=swap');
72
+
73
+ body {
74
+ font-family: 'Space Grotesk', sans-serif;
75
+ }
76
+
77
+ /* CSS existant... */
78
+ .uploadArea {
79
+ background: #f3f4f6;
80
+ border: 2px dashed #d1d5db;
81
+ transition: border-color 0.2s ease;
82
+ }
83
+ .uploadArea:hover {
84
+ border-color: #3b82f6;
85
+ }
86
+
87
+ .blue-button {
88
+ background: #3b82f6;
89
+ transition: background-color 0.2s ease;
90
+ }
91
+ .blue-button:hover {
92
+ background: #2563eb;
93
+ }
94
+
95
+ .loader {
96
+ width: 48px;
97
+ height: 48px;
98
+ border: 3px solid #3b82f6;
99
+ border-bottom-color: transparent;
100
+ border-radius: 50%;
101
+ display: inline-block;
102
+ animation: rotation 1s linear infinite;
103
+ }
104
+ @keyframes rotation {
105
+ 0% { transform: rotate(0deg); }
106
+ 100% { transform: rotate(360deg); }
107
+ }
108
+
109
+ .thought-box {
110
+ transition: max-height 0.3s ease-out;
111
+ max-height: 0;
112
+ overflow: hidden;
113
+ }
114
+ .thought-box.open {
115
+ max-height: 500px;
116
+ }
117
+
118
+ #thoughtsContent, #answerContent {
119
+ max-height: 500px;
120
+ overflow-y: auto;
121
+ scroll-behavior: smooth;
122
+ white-space: pre-wrap;
123
+ }
124
+
125
+ .preview-image {
126
+ max-width: 300px;
127
+ max-height: 300px;
128
+ object-fit: contain;
129
+ }
130
+
131
+ .timestamp {
132
+ color: #3b82f6;
133
+ font-size: 0.9em;
134
+ margin-left: 8px;
135
+ }
136
+
137
+ /* Styles pour les équations MathJax */
138
+ mjx-container {
139
+ display: inline-block;
140
+ overflow-x: auto;
141
+ overflow-y: hidden;
142
+ max-width: 100%;
143
+ padding: 0.5em 0;
144
+ min-width: auto !important;
145
+ }
146
+
147
+ mjx-container[jax="SVG"] {
148
+ direction: ltr;
149
+ }
150
+
151
+ mjx-container svg {
152
+ min-width: auto !important;
153
+ }
154
+
155
+ /* Amélioration pour les formules complexes */
156
+ mjx-mtable {
157
+ min-width: auto !important;
158
+ margin: 0 !important;
159
+ }
160
+
161
+ /* Support des tableaux et autres éléments */
162
+ table {
163
+ border-collapse: collapse;
164
+ width: 100%;
165
+ margin: 1rem 0;
166
+ }
167
+ th, td {
168
+ border: 1px solid #d1d5db;
169
+ padding: 0.75rem;
170
+ text-align: left;
171
+ }
172
+ th {
173
+ background-color: #f3f4f6;
174
+ font-weight: 600;
175
+ }
176
+
177
+ /* Intégration MathJax et tables */
178
+ mjx-container + table, table + mjx-container {
179
+ margin-top: 1rem;
180
+ }
181
+ </style>
182
+ </head>
183
+ <body class="p-4">
184
+ <div class="max-w-4xl mx-auto">
185
+ <header class="p-6 text-center mb-8">
186
+ <h1 class="text-4xl font-bold text-blue-600">Mariam M-0</h1>
187
+ <p class="text-gray-600">Solution Mathématique/Physique/Chimie Intelligente</p>
188
+ </header>
189
+
190
+ <main>
191
+ <!-- Le reste du HTML reste inchangé -->
192
+ <form id="problemForm" class="space-y-6" novalidate>
193
+ <!-- Zone de dépôt et sélection d'image -->
194
+ <div class="uploadArea p-8 text-center relative" aria-label="Zone de dépôt d'image">
195
+ <input type="file" id="imageInput" accept="image/*" class="absolute inset-0 w-full h-full opacity-0 cursor-pointer" aria-label="Choisir une image">
196
+ <div class="space-y-3">
197
+ <div class="w-16 h-16 mx-auto border-2 border-blue-400 rounded-full flex items-center justify-center">
198
+ <svg class="w-8 h-8 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
199
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
200
+ d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
201
+ </svg>
202
+ </div>
203
+ <p class="text-gray-700 font-medium">Déposez votre image ici</p>
204
+ <p class="text-gray-500 text-sm">ou cliquez pour sélectionner</p>
205
+ </div>
206
+ </div>
207
+
208
+ <!-- Aperçu de l'image -->
209
+ <div id="imagePreview" class="hidden text-center">
210
+ <img id="previewImage" class="preview-image mx-auto" alt="Prévisualisation de l'image sélectionnée">
211
+ </div>
212
+
213
+ <button type="submit" class="blue-button w-full py-3 text-white font-medium rounded-lg">
214
+ Résoudre le problème
215
+ </button>
216
+ </form>
217
+
218
+ <!-- Loader d'analyse -->
219
+ <div id="loader" class="hidden mt-8 text-center">
220
+ <span class="loader"></span>
221
+ <p class="mt-4 text-gray-600">Analyse en cours...</p>
222
+ </div>
223
+
224
+ <!-- Affichage de la solution -->
225
+ <section id="solution" class="hidden mt-8 space-y-6">
226
+ <div class="border-t pt-4">
227
+ <button id="thoughtsToggle" type="button" class="w-full flex justify-between items-center p-2">
228
+ <span class="font-medium text-gray-700">Processus de Réflexion</span>
229
+ <span id="timestamp" class="timestamp"></span>
230
+ </button>
231
+ <div id="thoughtsBox" class="thought-box">
232
+ <div id="thoughtsContent" class="p-4 text-gray-600"></div>
233
+ </div>
234
+ </div>
235
+ <div class="border-t pt-6">
236
+ <h3 class="text-xl font-bold text-gray-800 mb-4">Solution</h3>
237
+ <div id="answerContent" class="text-gray-700 table-responsive"></div>
238
+ </div>
239
+ </section>
240
+ </main>
241
+ </div>
242
+
243
+ <script>
244
+ document.addEventListener('DOMContentLoaded', () => {
245
+ // Récupération des éléments DOM
246
+ const form = document.getElementById('problemForm');
247
+ const imageInput = document.getElementById('imageInput');
248
+ const loader = document.getElementById('loader');
249
+ const solutionSection = document.getElementById('solution');
250
+ const thoughtsContent = document.getElementById('thoughtsContent');
251
+ const answerContent = document.getElementById('answerContent');
252
+ const thoughtsToggle = document.getElementById('thoughtsToggle');
253
+ const thoughtsBox = document.getElementById('thoughtsBox');
254
+ const imagePreview = document.getElementById('imagePreview');
255
+ const previewImage = document.getElementById('previewImage');
256
+ const timestamp = document.getElementById('timestamp');
257
+
258
+ let startTime = null;
259
+ let timerInterval = null;
260
+ let thoughtsBuffer = '';
261
+ let answerBuffer = '';
262
+ let currentMode = null;
263
+ let updateTimeout = null;
264
+ let mathJaxQueue = Promise.resolve();
265
+
266
+ // Mise à jour de l'affichage du temps écoulé
267
+ const updateTimestamp = () => {
268
+ if (startTime) {
269
+ const seconds = Math.floor((Date.now() - startTime) / 1000);
270
+ timestamp.textContent = `${seconds}s`;
271
+ }
272
+ };
273
+
274
+ const startTimer = () => {
275
+ startTime = Date.now();
276
+ timerInterval = setInterval(updateTimestamp, 1000);
277
+ updateTimestamp();
278
+ };
279
+
280
+ const stopTimer = () => {
281
+ clearInterval(timerInterval);
282
+ startTime = null;
283
+ timestamp.textContent = '';
284
+ };
285
+
286
+ // Gestion de la sélection ou du dépôt de l'image
287
+ const handleFileSelect = file => {
288
+ if (!file) return;
289
+ const reader = new FileReader();
290
+ reader.onload = e => {
291
+ previewImage.src = e.target.result;
292
+ imagePreview.classList.remove('hidden');
293
+ };
294
+ reader.readAsDataURL(file);
295
+ };
296
+
297
+ // Toggle pour afficher/cacher le processus de réflexion
298
+ thoughtsToggle.addEventListener('click', () => {
299
+ thoughtsBox.classList.toggle('open');
300
+ });
301
+
302
+ imageInput.addEventListener('change', e => handleFileSelect(e.target.files[0]));
303
+
304
+ // Gestion des événements de glisser-déposer
305
+ const dropZone = document.querySelector('.uploadArea');
306
+ dropZone.addEventListener('dragover', e => {
307
+ e.preventDefault();
308
+ dropZone.classList.add('border-blue-400');
309
+ });
310
+ dropZone.addEventListener('dragleave', e => {
311
+ e.preventDefault();
312
+ dropZone.classList.remove('border-blue-400');
313
+ });
314
+ dropZone.addEventListener('drop', e => {
315
+ e.preventDefault();
316
+ dropZone.classList.remove('border-blue-400');
317
+ handleFileSelect(e.dataTransfer.files[0]);
318
+ });
319
+
320
+ // Méthode améliorée pour traiter le LaTeX dans Markdown
321
+ const processContent = (content) => {
322
+ // Génération de jetons uniques pour éviter les collisions
323
+ const createToken = (index) => `__LATEX_${Date.now()}_${index}__`;
324
+
325
+ // Stockage des expressions LaTeX
326
+ const latexBlocks = [];
327
+
328
+ // Identification de tous les types d'expressions LaTeX
329
+ const latexPattern = /(\$\$[\s\S]*?\$\$|\$[^\$\n]+?\$|\\begin\{[a-z]*\}[\s\S]*?\\end\{[a-z]*\})/g;
330
+
331
+ // Étape 1: Remplacer les expressions LaTeX par des jetons
332
+ const tokenizedContent = content.replace(latexPattern, (match) => {
333
+ const token = createToken(latexBlocks.length);
334
+ latexBlocks.push({token, latex: match});
335
+ // Encadrer le jeton d'une manière qui empêche marked de le transformer
336
+ return `<!${token}!>`;
337
+ });
338
+
339
+ // Étape 2: Traiter avec marked
340
+ let htmlContent = marked.parse(tokenizedContent);
341
+
342
+ // Étape 3: Restaurer les expressions LaTeX
343
+ latexBlocks.forEach(({token, latex}) => {
344
+ // Échapper les caractères spéciaux dans le jeton
345
+ const escapedToken = token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
346
+ const tokenPattern = new RegExp(`<!${escapedToken}!>`, 'g');
347
+ htmlContent = htmlContent.replace(tokenPattern, latex);
348
+ });
349
+
350
+ return htmlContent;
351
+ };
352
+
353
+ // Fonction pour le rendu MathJax
354
+ const typesetMathJax = async (element) => {
355
+ if (!window.mathJaxReady) {
356
+ return new Promise((resolve) => {
357
+ document.addEventListener('mathjax-ready', () => {
358
+ typesetMathJax(element).then(resolve);
359
+ }, { once: true });
360
+ });
361
+ }
362
+
363
+ try {
364
+ // Utiliser une file d'attente pour éviter les conflits de rendu
365
+ return mathJaxQueue = mathJaxQueue.then(async () => {
366
+ try {
367
+ MathJax.startup.document.elements = [element];
368
+ await MathJax.typesetPromise([element]);
369
+ return true;
370
+ } catch (error) {
371
+ console.warn('Erreur dans le rendu MathJax', error);
372
+ // Essayer de réparer certaines expressions couramment problématiques
373
+ const fixLaTeX = (elem) => {
374
+ // Remplacer les caractères problématiques
375
+ elem.innerHTML = elem.innerHTML
376
+ .replace(/\\{/g, '\\lbrace ')
377
+ .replace(/\\}/g, '\\rbrace ')
378
+ .replace(/(?<![\\])\{/g, '\\{')
379
+ .replace(/(?<![\\])\}/g, '\\}');
380
+ };
381
+
382
+ // Appliquer les corrections et retenter
383
+ const mathElements = element.querySelectorAll('mjx-container');
384
+ mathElements.forEach(fixLaTeX);
385
+
386
+ try {
387
+ await MathJax.typesetPromise([element]);
388
+ return true;
389
+ } catch (retryError) {
390
+ console.error('Échec de la récupération MathJax', retryError);
391
+ return false;
392
+ }
393
+ }
394
+ });
395
+ } catch (fatalError) {
396
+ console.error('Erreur fatale MathJax', fatalError);
397
+ return false;
398
+ }
399
+ };
400
+
401
+ // Fonction pour mettre à jour l'affichage
402
+ const updateDisplay = async () => {
403
+ try {
404
+ // Appliquer le traitement au contenu
405
+ thoughtsContent.innerHTML = processContent(thoughtsBuffer);
406
+ answerContent.innerHTML = processContent(answerBuffer);
407
+
408
+ // Rendu MathJax
409
+ await typesetMathJax(thoughtsContent);
410
+ await typesetMathJax(answerContent);
411
+
412
+ // Scroll vers le bas
413
+ if (currentMode === 'thinking') {
414
+ thoughtsContent.scrollTop = thoughtsContent.scrollHeight;
415
+ } else if (currentMode === 'answering') {
416
+ answerContent.scrollTop = answerContent.scrollHeight;
417
+ }
418
+ } catch (error) {
419
+ console.error('Erreur lors de la mise à jour de l\'affichage', error);
420
+ }
421
+
422
+ // Réinitialiser le timeout
423
+ updateTimeout = null;
424
+ };
425
+
426
+ // Fonction pour planifier la mise à jour (dé-bounce)
427
+ const scheduleUpdate = () => {
428
+ if (updateTimeout) {
429
+ clearTimeout(updateTimeout);
430
+ }
431
+ updateTimeout = setTimeout(updateDisplay, 300);
432
+ };
433
+
434
+ // Configuration de marked
435
+ marked.setOptions({
436
+ gfm: true,
437
+ breaks: true,
438
+ pedantic: false,
439
+ smartLists: true
440
+ });
441
+
442
+ // Soumission du formulaire
443
+ form.addEventListener('submit', async e => {
444
+ e.preventDefault();
445
+ const file = imageInput.files[0];
446
+ if (!file) {
447
+ alert('Veuillez sélectionner une image.');
448
+ return;
449
+ }
450
+
451
+ // Initialisation de l'affichage et des variables
452
+ startTimer();
453
+ loader.classList.remove('hidden');
454
+ solutionSection.classList.add('hidden');
455
+ thoughtsContent.innerHTML = '';
456
+ answerContent.innerHTML = '';
457
+ thoughtsBuffer = '';
458
+ answerBuffer = '';
459
+ currentMode = null;
460
+ thoughtsBox.classList.add('open');
461
+
462
+ const formData = new FormData();
463
+ formData.append('image', file);
464
+
465
+ try {
466
+ // Envoi de la requête au serveur
467
+ const response = await fetch('/solve', {
468
+ method: 'POST',
469
+ body: formData
470
+ });
471
+
472
+ if (!response.ok) {
473
+ throw new Error(`Erreur HTTP: ${response.status}`);
474
+ }
475
+
476
+ const reader = response.body.getReader();
477
+ const decoder = new TextDecoder();
478
+ let buffer = '';
479
+
480
+ // Traitement d'un chunk de données
481
+ const processChunk = async chunk => {
482
+ buffer += decoder.decode(chunk, { stream: true });
483
+ // Recherche des délimiteurs de message complets
484
+ const lines = [];
485
+ let startPos = 0;
486
+ let endPos;
487
+
488
+ while ((endPos = buffer.indexOf('\n\n', startPos)) !== -1) {
489
+ lines.push(buffer.substring(startPos, endPos));
490
+ startPos = endPos + 2;
491
+ }
492
+
493
+ // Conserver la dernière partie potentiellement incomplète
494
+ buffer = buffer.substring(startPos);
495
+
496
+ for (const line of lines) {
497
+ if (!line.startsWith('data:')) continue;
498
+ try {
499
+ const data = JSON.parse(line.slice(5));
500
+
501
+ if (data.mode) {
502
+ currentMode = data.mode;
503
+ loader.classList.add('hidden');
504
+ solutionSection.classList.remove('hidden');
505
+ }
506
+ if (data.content) {
507
+ if (currentMode === 'thinking') {
508
+ thoughtsBuffer += data.content;
509
+ } else if (currentMode === 'answering') {
510
+ answerBuffer += data.content;
511
+ }
512
+ }
513
+ } catch (error) {
514
+ console.error('Erreur de parsing JSON:', error, line);
515
+ }
516
+ }
517
+
518
+ // Planifier la mise à jour
519
+ scheduleUpdate();
520
+ };
521
+
522
+ // Lecture du flux de réponse
523
+ while (true) {
524
+ const { done, value } = await reader.read();
525
+ if (done) {
526
+ // Traitement final si nécessaire
527
+ if (buffer.length > 5 && buffer.startsWith('data:')) {
528
+ try {
529
+ const data = JSON.parse(buffer.slice(5));
530
+ if (data.content) {
531
+ if (currentMode === 'thinking') {
532
+ thoughtsBuffer += data.content;
533
+ } else if (currentMode === 'answering') {
534
+ answerBuffer += data.content;
535
+ }
536
+ }
537
+ } catch (e) {
538
+ console.warn('Impossible de parser le dernier fragment:', e);
539
+ }
540
+ }
541
+
542
+ // Forcer une mise à jour finale
543
+ if (updateTimeout) {
544
+ clearTimeout(updateTimeout);
545
+ updateTimeout = null;
546
+ }
547
+ await updateDisplay();
548
+ break;
549
+ }
550
+ await processChunk(value);
551
+ }
552
+ stopTimer();
553
+ } catch (error) {
554
+ console.error('Erreur:', error);
555
+ alert(`Une erreur est survenue: ${error.message}`);
556
+ loader.classList.add('hidden');
557
+ stopTimer();
558
+ }
559
+ });
560
+
561
+ // Fonction de débogage pour tester le rendu LaTeX
562
+ window.testLatex = (expression) => {
563
+ const testDiv = document.createElement('div');
564
+ testDiv.innerHTML = expression;
565
+ document.body.appendChild(testDiv);
566
+
567
+ return typesetMathJax(testDiv)
568
+ .then(success => {
569
+ const result = {success, rendered: testDiv.innerHTML};
570
+ document.body.removeChild(testDiv);
571
+ return result;
572
+ });
573
+ };
574
+ });
575
+ </script>
576
+ </body>
577
+ </html>