Docfile commited on
Commit
58ef26b
·
verified ·
1 Parent(s): 81b84b5

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +218 -221
templates/index.html CHANGED
@@ -3,269 +3,266 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Analyseur de Documents IA</title>
7
- <!-- Include Tailwind CSS -->
8
  <script src="https://cdn.tailwindcss.com"></script>
 
 
 
9
  <style>
10
- /* Add a subtle background pattern or gradient if desired */
11
- body {
12
- background-color: #f8fafc; /* Tailwind gray-50 */
 
13
  }
14
- /* Custom style for smoother transitions */
15
- * {
16
- transition: all 0.2s ease-in-out;
17
  }
18
- /* Style for the analysis output to preserve formatting */
19
- #analysis-text {
20
- white-space: pre-wrap; /* Preserves whitespace and line breaks */
21
- word-wrap: break-word; /* Breaks long words */
22
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* Monospace font often looks good for code/analysis */
23
- line-height: 1.6;
24
  }
25
- /* Enhance radio button appearance */
26
- input[type="radio"]:checked {
27
- background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e");
28
- border-color: transparent;
29
- background-color: currentColor;
30
- background-size: 100% 100%;
31
- background-position: center;
32
- background-repeat: no-repeat;
33
  }
34
- input[type="radio"] {
35
- appearance: none;
36
- border-radius: 50%;
37
- width: 1.25em;
38
- height: 1.25em;
39
- border: 2px solid #cbd5e1; /* gray-300 */
40
- margin-right: 0.5em;
41
- vertical-align: middle; /* Align with text */
42
  }
43
- input[type="radio"]:checked {
44
- border-color: #2563eb; /* blue-600 */
45
- background-color: #2563eb; /* blue-600 */
 
 
46
  }
47
- input[type="radio"]:focus {
48
- outline: 2px solid transparent;
49
- outline-offset: 2px;
50
- box-shadow: 0 0 0 2px #fff, 0 0 0 4px #2563eb; /* blue-600 focus ring */
51
  }
52
 
53
- /* Loading Spinner Animation */
54
- @keyframes spin {
55
- to { transform: rotate(360deg); }
 
 
 
 
 
56
  }
57
- .spinner {
58
- display: inline-block;
59
- width: 1.5rem;
60
- height: 1.5rem;
61
- vertical-align: text-bottom;
62
- border: .25em solid currentColor;
63
- border-right-color: transparent;
64
- border-radius: 50%;
65
- animation: spin .75s linear infinite;
66
- color: #3b82f6; /* blue-500 */
67
  }
68
 
69
- /* File Input Styling */
70
- input[type="file"]::file-selector-button {
71
- margin-right: 1rem;
72
- display: inline-block;
73
- font-weight: 600; /* semibold */
74
- color: #1d4ed8; /* blue-700 */
75
- background-color: #eff6ff; /* blue-50 */
76
- padding: 0.5rem 1rem; /* py-2 px-4 */
77
- border-width: 1px;
78
- border-color: transparent;
79
- border-radius: 0.375rem; /* rounded-md */
80
- cursor: pointer;
81
- transition: background-color 0.2s ease-in-out;
82
  }
83
 
84
- input[type="file"]::file-selector-button:hover {
85
- background-color: #dbeafe; /* blue-100 */
 
 
 
86
  }
87
 
 
 
 
 
88
  </style>
89
  </head>
90
- <body class="font-sans antialiased text-gray-800">
91
-
92
- <div class="container mx-auto max-w-4xl p-6 md:p-10">
 
 
 
93
 
94
- <header class="text-center mb-10">
95
- <h1 class="text-3xl md:text-4xl font-bold text-gray-900 mb-2">Analyseur de Documents IA</h1>
96
- <p class="text-md text-gray-600">Téléchargez une image ou un fichier texte pour obtenir une analyse détaillée.</p>
 
 
 
 
 
 
97
  </header>
98
 
99
- <main>
100
- <div class="bg-white p-6 md:p-8 rounded-xl shadow-lg border border-gray-200">
101
- <form id="upload-form" enctype="multipart/form-data">
102
- <div class="mb-6">
103
- <label for="file" class="block text-lg font-semibold text-gray-700 mb-2">1. Choisissez votre fichier :</label>
104
- <input type="file" id="file" name="file" required
105
- class="block w-full text-sm text-gray-600 bg-gray-50 border border-gray-300 rounded-lg cursor-pointer focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent p-2">
106
- <p class="mt-1 text-xs text-gray-500">Taille max : 16 Mo. Types supportés : Images (JPG, PNG, GIF), Texte (TXT).</p>
107
- </div>
 
 
 
 
 
 
 
 
 
 
 
108
 
109
- <div class="mb-8">
110
- <label class="block text-lg font-semibold text-gray-700 mb-3">2. Quel type de document est-ce ?</label>
111
- <div class="flex items-center space-x-6">
112
- <label for="image" class="flex items-center cursor-pointer">
113
- <input type="radio" id="image" name="fileType" value="image" checked
114
- class="focus:ring-blue-500 text-blue-600">
115
- <span class="ml-2 text-gray-700">Image Iconographique</span>
116
- </label>
117
- <label for="text" class="flex items-center cursor-pointer">
118
- <input type="radio" id="text" name="fileType" value="text"
119
- class="focus:ring-blue-500 text-blue-600">
120
- <span class="ml-2 text-gray-700">Texte</span>
121
- </label>
122
- </div>
123
  </div>
124
-
125
- <div class="text-center">
126
- <button type="submit" id="submit-btn"
127
- class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-8 rounded-lg shadow-md transition duration-300 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50 disabled:opacity-50 disabled:cursor-not-allowed">
128
- Lancer l'Analyse
129
- </button>
130
  </div>
131
- </form>
132
-
133
- <!-- Loading Indicator -->
134
- <div id="loading" class="hidden text-center my-8">
135
- <div class="spinner" role="status">
136
- <span class="sr-only">Loading...</span>
137
- </div>
138
- <p class="text-gray-600 mt-2 font-medium">Analyse en cours, veuillez patienter...</p>
139
  </div>
140
-
141
- <!-- Error Message Area -->
142
- <div id="error-message" class="hidden mt-6 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-lg relative" role="alert">
143
- <strong class="font-bold">Erreur :</strong>
144
- <span class="block sm:inline" id="error-text"></span>
145
- </div>
146
-
147
  </div>
148
 
149
- <!-- Results Area -->
150
- <div id="results" class="hidden mt-10 bg-white p-6 md:p-8 rounded-xl shadow-lg border border-gray-200">
151
- <h2 class="text-2xl font-semibold mb-6 text-gray-800 border-b pb-3">Résultats de l'Analyse</h2>
152
-
153
- <!-- Container for optional image preview -->
154
- <div id="uploaded-image-container" class="mb-6 hidden">
155
- <h3 class="text-xl font-semibold text-gray-700 mb-3">Image Analysée :</h3>
156
- <img id="uploaded-image" src="" alt="Image téléchargée" class="max-w-md mx-auto h-auto rounded-lg shadow-md border border-gray-200">
157
  </div>
 
158
 
159
- <!-- Container for the analysis text -->
160
- <div id="analysis-container">
161
- <h3 class="text-xl font-semibold text-gray-700 mb-3">Analyse Détaillée :</h3>
162
- <div id="analysis-text" class="bg-gray-50 p-4 rounded-md border border-gray-200 text-sm md:text-base">
163
- <!-- Analysis will be loaded here by JavaScript -->
164
- </div>
165
  </div>
166
  </div>
167
- </main>
 
168
 
169
- <footer class="text-center mt-12 text-sm text-gray-500">
170
- Propulsé par Google Gemini & Flask.
171
- </footer>
 
 
 
172
  </div>
173
 
174
  <script>
175
- const uploadForm = document.getElementById('upload-form');
176
- const submitButton = document.getElementById('submit-btn');
177
- const loadingIndicator = document.getElementById('loading');
178
- const resultsDiv = document.getElementById('results');
179
- const analysisTextDiv = document.getElementById('analysis-text');
180
- const uploadedImageContainer = document.getElementById('uploaded-image-container');
181
- const uploadedImage = document.getElementById('uploaded-image');
182
- const errorMessageDiv = document.getElementById('error-message');
183
- const errorTextSpan = document.getElementById('error-text');
184
-
185
- uploadForm.addEventListener('submit', async (event) => {
186
- event.preventDefault(); // Prevent default form submission
187
-
188
- // Reset UI
189
- resultsDiv.classList.add('hidden');
190
- errorMessageDiv.classList.add('hidden');
191
- uploadedImageContainer.classList.add('hidden');
192
- loadingIndicator.classList.remove('hidden');
193
- submitButton.disabled = true;
194
- submitButton.textContent = 'Analyse en cours...';
195
-
196
- const formData = new FormData(uploadForm);
197
- const fileInput = document.getElementById('file');
198
- const fileType = formData.get('fileType'); // Get selected radio button value
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
 
200
- // Basic client-side validation (optional, backend validation is key)
201
- if (!fileInput.files || fileInput.files.length === 0) {
202
- showError('Veuillez sélectionner un fichier.');
203
- resetButton();
204
- loadingIndicator.classList.add('hidden');
205
- return;
206
- }
207
 
 
 
208
 
209
- try {
210
- const response = await fetch('/upload', {
211
  method: 'POST',
212
- body: formData // FormData handles headers automatically for multipart/form-data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  });
214
-
215
- loadingIndicator.classList.add('hidden'); // Hide loading indicator regardless of outcome
216
-
217
- if (!response.ok) {
218
- let errorMsg = `Erreur HTTP ${response.status}: ${response.statusText}`;
219
- try {
220
- const errorData = await response.json();
221
- errorMsg = errorData.error || 'Une erreur inconnue est survenue côté serveur.';
222
- } catch (e) {
223
- // If response is not JSON, use the status text
224
- }
225
- throw new Error(errorMsg);
226
- }
227
-
228
- const result = await response.json();
229
-
230
- if (result.success) {
231
- analysisTextDiv.textContent = result.analysis; // Use textContent to prevent XSS with raw HTML
232
-
233
- // Display image only if it was an image upload and a URL is provided
234
- if (fileType === 'image' && result.file_url) {
235
- uploadedImage.src = result.file_url + '?t=' + new Date().getTime(); // Add timestamp to bypass cache if needed
236
- uploadedImageContainer.classList.remove('hidden');
237
- } else {
238
- uploadedImageContainer.classList.add('hidden'); // Ensure it's hidden for text uploads
239
- }
240
-
241
- resultsDiv.classList.remove('hidden'); // Show results section
242
- // Scroll to results smoothly
243
- resultsDiv.scrollIntoView({ behavior: 'smooth', block: 'start' });
244
- } else {
245
- throw new Error(result.error || 'Une erreur inattendue est survenue.');
246
- }
247
-
248
- } catch (error) {
249
- console.error('Upload Error:', error);
250
- showError(error.message || 'Une erreur de communication est survenue.');
251
- } finally {
252
- resetButton();
253
  }
254
  });
255
-
256
- function showError(message) {
257
- errorTextSpan.textContent = message;
258
- errorMessageDiv.classList.remove('hidden');
259
- loadingIndicator.classList.add('hidden'); // Ensure loading is hidden on error
260
- errorMessageDiv.scrollIntoView({ behavior: 'smooth', block: 'center' });
261
- }
262
-
263
- function resetButton() {
264
- submitButton.disabled = false;
265
- submitButton.textContent = "Lancer l'Analyse";
266
- }
267
-
268
  </script>
269
-
270
  </body>
271
- </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Mariam Espagnol - Analyse de Documents</title>
 
7
  <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://unpkg.com/[email protected]/marked.min.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prismjs/1.24.1/prism.min.js"></script>
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prismjs/1.24.1/themes/prism.min.css">
11
  <style>
12
+ @import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&family=Poppins:wght@300;400;500;600&display=swap');
13
+
14
+ .fade-in {
15
+ animation: fadeIn 0.5s ease-in;
16
  }
17
+
18
+ .slide-up {
19
+ animation: slideUp 0.5s ease-out;
20
  }
21
+
22
+ @keyframes fadeIn {
23
+ from { opacity: 0; }
24
+ to { opacity: 1; }
 
 
25
  }
26
+
27
+ @keyframes slideUp {
28
+ from { transform: translateY(20px); opacity: 0; }
29
+ to { transform: translateY(0); opacity: 1; }
 
 
 
 
30
  }
31
+
32
+ .gradient-text {
33
+ background: linear-gradient(45deg, #FF6B6B, #4ECDC4);
34
+ -webkit-background-clip: text;
35
+ background-clip: text;
36
+ color: transparent;
 
 
37
  }
38
+
39
+ .upload-zone {
40
+ background: linear-gradient(145deg, #ffffff, #f3f4f6);
41
+ box-shadow: 20px 20px 60px #d1d1d1,
42
+ -20px -20px 60px #ffffff;
43
  }
44
+
45
+ .upload-zone:hover {
46
+ transform: scale(1.01);
47
+ transition: all 0.3s ease;
48
  }
49
 
50
+ .waves {
51
+ position: absolute;
52
+ bottom: 0;
53
+ left: 0;
54
+ width: 100%;
55
+ overflow: hidden;
56
+ line-height: 0;
57
+ transform: rotate(180deg);
58
  }
59
+
60
+ .waves svg {
61
+ position: relative;
62
+ display: block;
63
+ width: calc(100% + 1.3px);
64
+ height: 150px;
 
 
 
 
65
  }
66
 
67
+ .prose {
68
+ max-width: none;
69
+ color: #374151;
 
 
 
 
 
 
 
 
 
 
70
  }
71
 
72
+ .prose h1, .prose h2, .prose h3 {
73
+ color: #1F2937;
74
+ font-family: 'Playfair Display', serif;
75
+ margin-top: 2em;
76
+ margin-bottom: 1em;
77
  }
78
 
79
+ .prose p {
80
+ margin-bottom: 1.5em;
81
+ line-height: 1.8;
82
+ }
83
  </style>
84
  </head>
85
+ <body class="bg-gradient-to-br from-gray-50 to-gray-100 min-h-screen relative">
86
+ <div class="waves">
87
+ <svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 120" preserveAspectRatio="none">
88
+ <path d="M321.39,56.44c58-10.79,114.16-30.13,172-41.86,82.39-16.72,168.19-17.73,250.45-.39C823.78,31,906.67,72,985.66,92.83c70.05,18.48,146.53,26.09,214.34,3V0H0V27.35A600.21,600.21,0,0,0,321.39,56.44Z" class="fill-indigo-50"></path>
89
+ </svg>
90
+ </div>
91
 
92
+ <div class="max-w-6xl mx-auto px-4 py-12 relative z-10">
93
+ <!-- Header -->
94
+ <header class="text-center mb-16 fade-in">
95
+ <h1 class="text-5xl md:text-6xl font-bold mb-4 font-['Playfair_Display'] gradient-text">
96
+ Mariam Espagnol
97
+ </h1>
98
+ <p class="text-xl text-gray-600 font-light max-w-2xl mx-auto">
99
+ Analysez vos documents en espagnol avec une traduction française précise et élégante
100
+ </p>
101
  </header>
102
 
103
+ <!-- Main Content -->
104
+ <div class="bg-white rounded-2xl shadow-xl p-6 md:p-8 mb-8 slide-up">
105
+ <!-- File Type Selection -->
106
+ <div class="mb-8">
107
+ <label class="block text-lg font-medium text-gray-700 mb-4">Type de document</label>
108
+ <div class="flex gap-6">
109
+ <label class="relative flex items-center group cursor-pointer">
110
+ <input type="radio" name="fileType" value="image" checked
111
+ class="peer sr-only">
112
+ <div class="w-6 h-6 border-2 border-gray-300 rounded-full peer-checked:border-indigo-500 peer-checked:bg-indigo-500 transition-all"></div>
113
+ <span class="ml-3 text-gray-700 group-hover:text-indigo-500 transition-colors">Image</span>
114
+ </label>
115
+ <label class="relative flex items-center group cursor-pointer">
116
+ <input type="radio" name="fileType" value="text"
117
+ class="peer sr-only">
118
+ <div class="w-6 h-6 border-2 border-gray-300 rounded-full peer-checked:border-indigo-500 peer-checked:bg-indigo-500 transition-all"></div>
119
+ <span class="ml-3 text-gray-700 group-hover:text-indigo-500 transition-colors">Texte</span>
120
+ </label>
121
+ </div>
122
+ </div>
123
 
124
+ <!-- Upload Zone -->
125
+ <div class="upload-zone border-2 border-dashed border-gray-300 rounded-2xl p-8 text-center cursor-pointer transition-all duration-300 relative overflow-hidden">
126
+ <div class="space-y-4">
127
+ <div class="w-20 h-20 mx-auto rounded-full bg-indigo-50 flex items-center justify-center">
128
+ <svg class="w-10 h-10 text-indigo-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
129
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>
130
+ </svg>
 
 
 
 
 
 
 
131
  </div>
132
+ <div class="text-sm text-gray-600">
133
+ <label class="relative cursor-pointer rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500">
134
+ <span>Déposez votre fichier ici</span>
135
+ <input id="file-upload" name="file" type="file" class="sr-only">
136
+ </label>
137
+ <p class="pl-1">ou cliquez pour sélectionner</p>
138
  </div>
139
+ <p class="text-xs text-gray-500">PNG, JPG, GIF, PDF, TXT jusqu'à 10MB</p>
 
 
 
 
 
 
 
140
  </div>
 
 
 
 
 
 
 
141
  </div>
142
 
143
+ <!-- Preview -->
144
+ <div id="preview" class="mt-12 hidden slide-up">
145
+ <h3 class="text-xl font-medium text-gray-900 mb-6">Aperçu du document</h3>
146
+ <div class="preview-container border rounded-xl p-6 bg-gray-50">
147
+ <img id="image-preview" class="max-w-full h-auto hidden rounded-lg shadow-lg" alt="Aperçu">
148
+ <pre id="text-preview" class="text-sm text-gray-700 whitespace-pre-wrap hidden bg-white p-4 rounded-lg shadow-inner"></pre>
 
 
149
  </div>
150
+ </div>
151
 
152
+ <!-- Analysis Results -->
153
+ <div id="results" class="mt-12 hidden slide-up">
154
+ <h3 class="text-xl font-medium text-gray-900 mb-6">Analyse détaillée</h3>
155
+ <div class="bg-white rounded-xl p-6 shadow-inner">
156
+ <div id="analysis-content" class="prose prose-indigo"></div>
 
157
  </div>
158
  </div>
159
+ </div>
160
+ </div>
161
 
162
+ <!-- Loading Spinner -->
163
+ <div id="loading" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden backdrop-blur-sm z-50">
164
+ <div class="bg-white p-8 rounded-2xl shadow-2xl">
165
+ <div class="animate-spin rounded-full h-16 w-16 border-t-4 border-b-4 border-indigo-500 mx-auto"></div>
166
+ <p class="text-gray-700 mt-4">Analyse en cours...</p>
167
+ </div>
168
  </div>
169
 
170
  <script>
171
+ document.addEventListener('DOMContentLoaded', function() {
172
+ const uploadZone = document.querySelector('.upload-zone');
173
+ const fileInput = document.getElementById('file-upload');
174
+ const preview = document.getElementById('preview');
175
+ const imagePreview = document.getElementById('image-preview');
176
+ const textPreview = document.getElementById('text-preview');
177
+ const results = document.getElementById('results');
178
+ const analysisContent = document.getElementById('analysis-content');
179
+ const loading = document.getElementById('loading');
180
+
181
+ // Configuration de marked pour le rendu Markdown
182
+ marked.setOptions({
183
+ breaks: true,
184
+ gfm: true,
185
+ headerIds: true,
186
+ sanitize: false
187
+ });
188
+
189
+ uploadZone.addEventListener('click', () => fileInput.click());
190
+
191
+ uploadZone.addEventListener('dragover', (e) => {
192
+ e.preventDefault();
193
+ uploadZone.classList.add('border-indigo-500', 'bg-indigo-50');
194
+ });
195
+
196
+ uploadZone.addEventListener('dragleave', () => {
197
+ uploadZone.classList.remove('border-indigo-500', 'bg-indigo-50');
198
+ });
199
+
200
+ uploadZone.addEventListener('drop', (e) => {
201
+ e.preventDefault();
202
+ uploadZone.classList.remove('border-indigo-500', 'bg-indigo-50');
203
+ const files = e.dataTransfer.files;
204
+ if (files.length) handleFile(files[0]);
205
+ });
206
+
207
+ fileInput.addEventListener('change', (e) => {
208
+ if (e.target.files.length) handleFile(e.target.files[0]);
209
+ });
210
+
211
+ function handleFile(file) {
212
+ const fileType = document.querySelector('input[name="fileType"]:checked').value;
213
+
214
+ // Animation de l'aperçu
215
+ preview.classList.remove('hidden');
216
+ preview.classList.add('fade-in');
217
+
218
+ if (fileType === 'image') {
219
+ imagePreview.classList.remove('hidden');
220
+ textPreview.classList.add('hidden');
221
+ const reader = new FileReader();
222
+ reader.onload = (e) => {
223
+ imagePreview.src = e.target.result;
224
+ imagePreview.classList.add('fade-in');
225
+ };
226
+ reader.readAsDataURL(file);
227
+ } else {
228
+ imagePreview.classList.add('hidden');
229
+ textPreview.classList.remove('hidden');
230
+ const reader = new FileReader();
231
+ reader.onload = (e) => {
232
+ textPreview.textContent = e.target.result;
233
+ textPreview.classList.add('fade-in');
234
+ };
235
+ reader.readAsText(file);
236
+ }
237
 
238
+ const formData = new FormData();
239
+ formData.append('file', file);
240
+ formData.append('fileType', fileType);
 
 
 
 
241
 
242
+ loading.classList.remove('hidden');
243
+ loading.classList.add('fade-in');
244
 
245
+ fetch('/upload', {
 
246
  method: 'POST',
247
+ body: formData
248
+ })
249
+ .then(response => response.json())
250
+ .then(data => {
251
+ loading.classList.add('hidden');
252
+ results.classList.remove('hidden');
253
+ results.classList.add('slide-up');
254
+ // Convertir le Markdown en HTML
255
+ analysisContent.innerHTML = marked.parse(data.analysis);
256
+ // Activer la coloration syntaxique
257
+ Prism.highlightAll();
258
+ })
259
+ .catch(error => {
260
+ loading.classList.add('hidden');
261
+ alert('Une erreur est survenue lors de l\'analyse. Veuillez réessayer.');
262
+ console.error('Error:', error);
263
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  }
265
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  </script>
 
267
  </body>
268
+ </html>