victorafarias commited on
Commit
5369c45
·
1 Parent(s): 17910ab

Correçõe na conversão posterior do texto para md

Browse files
Files changed (1) hide show
  1. templates/index.html +96 -33
templates/index.html CHANGED
@@ -27,7 +27,6 @@
27
  </style>
28
  </head>
29
  <body>
30
-
31
  <div id="loader-overlay" style="display: none;">
32
  <div class="loader-content">
33
  <div class="loader-spinner"></div>
@@ -35,9 +34,7 @@
35
  <div class="progress-bar-container"><div id="progress-bar" class="progress-bar"></div></div>
36
  </div>
37
  </div>
38
-
39
  <button id="merge-btn" class="floating-merge-btn" style="display: none;">Processar Merge</button>
40
-
41
  <div class="container">
42
  <div class="header-container">
43
  <div>
@@ -58,9 +55,7 @@
58
  <button class="refresh-btn" onclick="window.location.href='/'" title="Limpar e começar de novo">Nova Consulta</button>
59
  </div>
60
  </div>
61
-
62
  <div id="error-box-container"></div>
63
-
64
  <div id="real-form-container">
65
  <form id="request-form-real">
66
  <label for="solicitacao_usuario">Digite sua solicitação (ou arraste arquivos aqui):</label>
@@ -73,17 +68,13 @@
73
  <form id="request-form-mock">
74
  <label for="mock_text">Cole o texto de simulação aqui:</label>
75
  <textarea name="mock_text" id="mock_text" rows="10" required>### Título
76
-
77
  Este é um exemplo de texto **bruto** em Markdown.
78
-
79
  - Item 1
80
  - Item 2
81
-
82
  Use o botão `Converter para MD` para ver a mágica.</textarea>
83
  <button type="submit">Simular Resposta</button>
84
  </form>
85
  </div>
86
-
87
  <div id="results-container" class="results-container" style="display: none;">
88
  <div class="result-column">
89
  <div class="column-header">
@@ -106,6 +97,7 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
106
  <div class="output-box" id="sonnet-output"></div>
107
  </div>
108
  <div class="result-column">
 
109
  <div class="column-header">
110
  <h2>Gemini</h2>
111
  <div>
@@ -116,7 +108,6 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
116
  <div class="output-box" id="gemini-output"></div>
117
  </div>
118
  </div>
119
-
120
  <div id="final-result-container" style="display: none;">
121
  <div class="column-header" style="border-radius: 8px 8px 0 0; background-color: #e9ecef;">
122
  <h2 id="final-result-title">Texto Final</h2>
@@ -128,7 +119,6 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
128
  <div class="output-box" id="final-output" style="background-color: #fafafa; border: 1px solid #e0e0e0; border-top: none; border-radius: 0 0 8px 8px;"></div>
129
  </div>
130
  </div>
131
-
132
  <script>
133
  // --- Variáveis Globais ---
134
  const processingModeSwitch = document.getElementById('processing-mode-switch');
@@ -149,6 +139,11 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
149
  let originalUserQuery = "";
150
  let rawTexts = {}; // NOVO: Objeto para guardar os textos brutos
151
 
 
 
 
 
 
152
  // --- Lógica de UI ---
153
  modeSwitch.addEventListener('change', function() {
154
  realContainer.style.display = this.checked ? 'none' : 'block';
@@ -191,13 +186,15 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
191
  fileList.appendChild(li);
192
  });
193
  }
194
-
195
  // --- Lógica de Submissão Principal ---
196
  document.getElementById('request-form-real').addEventListener('submit', handleFormSubmit);
197
  document.getElementById('request-form-mock').addEventListener('submit', handleFormSubmit);
198
-
199
  async function handleFormSubmit(event) {
200
  event.preventDefault();
 
 
201
  // Resetar a interface
202
  errorContainer.innerHTML = '';
203
  resultsContainer.style.display = 'none';
@@ -209,48 +206,80 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
209
  btn.innerText = 'Converter para MD';
210
  });
211
  rawTexts = {}; // Limpa os textos brutos
212
-
 
213
  // Iniciar o loader
214
  loaderMessage.textContent = 'Iniciando conexão...';
215
  progressBar.style.width = '0%';
216
  loader.style.display = 'flex';
217
-
 
218
  const formData = new FormData();
219
  formData.append('processing_mode', processingModeSwitch.checked ? 'atomic' : 'hierarchical');
 
220
  if (modeSwitch.checked) {
221
  formData.append('mode', 'test');
222
  formData.append('mock_text', document.getElementById('mock_text').value);
223
  originalUserQuery = "Simulação de teste.";
 
224
  } else {
225
  formData.append('mode', 'real');
226
  originalUserQuery = document.getElementById('solicitacao_usuario').value;
227
  formData.append('solicitacao', originalUserQuery);
228
  attachedFiles.forEach(file => { formData.append('files', file); });
 
229
  }
 
230
  try {
 
231
  const response = await fetch('/process', { method: 'POST', body: formData });
232
  if (!response.ok || !response.body) throw new Error(`Erro na resposta do servidor: ${response.statusText}`);
233
 
 
234
  const reader = response.body.getReader();
235
  const decoder = new TextDecoder();
 
 
236
  while (true) {
237
  const { done, value } = await reader.read();
238
- if (done) break;
 
 
 
 
239
  const chunk = decoder.decode(value, { stream: true });
240
- const lines = chunk.split('\n\n');
 
 
 
 
 
 
241
  lines.forEach(line => {
242
  if (line.startsWith('data: ')) {
243
- const jsonData = line.substring(6);
244
- if (jsonData.trim()) {
 
245
  try {
246
  const data = JSON.parse(jsonData);
 
247
  processStreamData(data, false);
248
- } catch (e) { console.error("Erro ao parsear JSON:", jsonData); }
 
 
 
249
  }
250
  }
251
  });
252
  }
 
 
 
 
 
 
253
  } catch (error) {
 
254
  showError('A conexão com o servidor falhou.');
255
  loader.style.display = 'none';
256
  console.error("Fetch Error:", error);
@@ -259,11 +288,12 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
259
 
260
  // --- Lógica do Botão de Merge ---
261
  mergeBtn.addEventListener('click', async function() {
 
262
  loaderMessage.textContent = 'Processando o merge dos textos...';
263
  progressBar.style.width = '0%';
264
  loader.style.display = 'flex';
265
  this.style.display = 'none';
266
-
267
  // Sempre pega o texto bruto para o merge
268
  const payload = {
269
  solicitacao_usuario: originalUserQuery,
@@ -271,6 +301,7 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
271
  sonnet_text: rawTexts['sonnet-output'] || '',
272
  gemini_text: rawTexts['gemini-output'] || '',
273
  };
 
274
 
275
  try {
276
  const response = await fetch('/merge', {
@@ -300,6 +331,7 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
300
  });
301
  }
302
  } catch (error) {
 
303
  showError("A conexão falhou ao tentar processar o merge.");
304
  loader.style.display = 'none';
305
  }
@@ -307,23 +339,41 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
307
 
308
  // --- Função de Processamento de Stream (ATUALIZADA) ---
309
  function processStreamData(data, isMerge) {
 
 
 
 
 
 
 
310
  if (data.error) {
 
311
  showError(data.error);
312
  loader.style.display = 'none';
313
  return;
314
  }
315
-
316
- loaderMessage.textContent = data.message;
317
- progressBar.style.width = data.progress + '%';
 
 
 
318
 
319
  const processContent = (targetId, content, wordCount = null) => {
 
 
 
320
  const targetBox = document.getElementById(targetId);
321
- if (!targetBox) return;
 
 
 
322
 
323
  // Armazena o texto bruto e exibe na tela
324
  rawTexts[targetId] = content;
325
- targetBox.innerText = content;
326
-
 
327
  if (targetId === 'final-output') {
328
  const finalTitle = document.getElementById('final-result-title');
329
  finalTitle.textContent = `Texto Final`;
@@ -331,22 +381,28 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
331
  finalTitle.textContent += ` (${wordCount} palavras)`;
332
  }
333
  finalResultContainer.style.display = 'block';
 
334
  } else {
335
- resultsContainer.style.display = 'flex';
 
336
  }
337
  };
338
 
339
  if (isMerge && data.final_result) {
 
340
  processContent('final-output', data.final_result.content, data.final_result.word_count);
341
  } else if (data.partial_result) {
 
342
  processContent(data.partial_result.id, data.partial_result.content);
343
  }
344
 
345
  if (data.done) {
 
346
  setTimeout(() => {
347
  loader.style.display = 'none';
348
  if (data.mode === 'atomic' && !isMerge) {
349
  mergeBtn.style.display = 'block';
 
350
  }
351
  }, 500);
352
  }
@@ -354,29 +410,35 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
354
 
355
  // --- Funções de Utilitários (ATUALIZADAS) ---
356
  function showError(message) {
 
357
  errorContainer.innerHTML = `<div class="error-box"><strong>Erro:</strong> ${message}<span class="close-btn-error" onclick="this.parentElement.style.display='none';" title="Fechar">&times;</span></div>`;
358
  }
359
 
360
  function copyToClipboard(elementId) {
 
361
  // Sempre copia o texto bruto original do objeto rawTexts
362
  const textToCopy = rawTexts[elementId];
363
  if (textToCopy !== undefined) {
364
- navigator.clipboard.writeText(textToCopy).then(() => {
365
- alert('Texto copiado!');
 
366
  });
367
  } else {
 
368
  alert('Nenhum texto para copiar.');
369
  }
370
  }
371
 
372
  // NOVA FUNÇÃO para converter para Markdown sob demanda
373
  async function convertToMarkdown(elementId) {
 
374
  const button = event.target;
375
  button.disabled = true;
376
  button.innerText = 'Convertendo...';
377
-
378
  const rawText = rawTexts[elementId];
379
  if (rawText === undefined) {
 
380
  showError('Não há texto para converter.');
381
  button.innerText = 'Converter para MD';
382
  button.disabled = false;
@@ -389,20 +451,21 @@ Use o botão `Converter para MD` para ver a mágica.</textarea>
389
  headers: { 'Content-Type': 'application/json' },
390
  body: JSON.stringify({ text: rawText })
391
  });
392
-
393
  if (!response.ok) throw new Error('Falha na conversão');
394
 
395
  const data = await response.json();
396
  const targetBox = document.getElementById(elementId);
397
  targetBox.innerHTML = data.html; // Insere o HTML convertido
398
  button.innerText = 'Convertido'; // Mantém o botão desabilitado para indicar sucesso
 
399
  } catch (error) {
400
  showError('Não foi possível converter o texto.');
401
  console.error('Conversion error:', error);
 
402
  button.innerText = 'Converter para MD'; // Reabilita em caso de erro
403
  button.disabled = false;
404
  }
405
  }
406
  </script>
407
  </body>
408
- </html>
 
27
  </style>
28
  </head>
29
  <body>
 
30
  <div id="loader-overlay" style="display: none;">
31
  <div class="loader-content">
32
  <div class="loader-spinner"></div>
 
34
  <div class="progress-bar-container"><div id="progress-bar" class="progress-bar"></div></div>
35
  </div>
36
  </div>
 
37
  <button id="merge-btn" class="floating-merge-btn" style="display: none;">Processar Merge</button>
 
38
  <div class="container">
39
  <div class="header-container">
40
  <div>
 
55
  <button class="refresh-btn" onclick="window.location.href='/'" title="Limpar e começar de novo">Nova Consulta</button>
56
  </div>
57
  </div>
 
58
  <div id="error-box-container"></div>
 
59
  <div id="real-form-container">
60
  <form id="request-form-real">
61
  <label for="solicitacao_usuario">Digite sua solicitação (ou arraste arquivos aqui):</label>
 
68
  <form id="request-form-mock">
69
  <label for="mock_text">Cole o texto de simulação aqui:</label>
70
  <textarea name="mock_text" id="mock_text" rows="10" required>### Título
 
71
  Este é um exemplo de texto **bruto** em Markdown.
 
72
  - Item 1
73
  - Item 2
 
74
  Use o botão `Converter para MD` para ver a mágica.</textarea>
75
  <button type="submit">Simular Resposta</button>
76
  </form>
77
  </div>
 
78
  <div id="results-container" class="results-container" style="display: none;">
79
  <div class="result-column">
80
  <div class="column-header">
 
97
  <div class="output-box" id="sonnet-output"></div>
98
  </div>
99
  <div class="result-column">
100
+ <div class="result-column">
101
  <div class="column-header">
102
  <h2>Gemini</h2>
103
  <div>
 
108
  <div class="output-box" id="gemini-output"></div>
109
  </div>
110
  </div>
 
111
  <div id="final-result-container" style="display: none;">
112
  <div class="column-header" style="border-radius: 8px 8px 0 0; background-color: #e9ecef;">
113
  <h2 id="final-result-title">Texto Final</h2>
 
119
  <div class="output-box" id="final-output" style="background-color: #fafafa; border: 1px solid #e0e0e0; border-top: none; border-radius: 0 0 8px 8px;"></div>
120
  </div>
121
  </div>
 
122
  <script>
123
  // --- Variáveis Globais ---
124
  const processingModeSwitch = document.getElementById('processing-mode-switch');
 
139
  let originalUserQuery = "";
140
  let rawTexts = {}; // NOVO: Objeto para guardar os textos brutos
141
 
142
+ // Log para debug
143
+ function debugLog(message) {
144
+ console.log(`[FRONTEND DEBUG] ${message}`);
145
+ }
146
+
147
  // --- Lógica de UI ---
148
  modeSwitch.addEventListener('change', function() {
149
  realContainer.style.display = this.checked ? 'none' : 'block';
 
186
  fileList.appendChild(li);
187
  });
188
  }
189
+
190
  // --- Lógica de Submissão Principal ---
191
  document.getElementById('request-form-real').addEventListener('submit', handleFormSubmit);
192
  document.getElementById('request-form-mock').addEventListener('submit', handleFormSubmit);
193
+
194
  async function handleFormSubmit(event) {
195
  event.preventDefault();
196
+ debugLog("=== FORM SUBMIT INICIADO ===");
197
+
198
  // Resetar a interface
199
  errorContainer.innerHTML = '';
200
  resultsContainer.style.display = 'none';
 
206
  btn.innerText = 'Converter para MD';
207
  });
208
  rawTexts = {}; // Limpa os textos brutos
209
+ debugLog("Interface resetada");
210
+
211
  // Iniciar o loader
212
  loaderMessage.textContent = 'Iniciando conexão...';
213
  progressBar.style.width = '0%';
214
  loader.style.display = 'flex';
215
+ debugLog("Loader iniciado");
216
+
217
  const formData = new FormData();
218
  formData.append('processing_mode', processingModeSwitch.checked ? 'atomic' : 'hierarchical');
219
+
220
  if (modeSwitch.checked) {
221
  formData.append('mode', 'test');
222
  formData.append('mock_text', document.getElementById('mock_text').value);
223
  originalUserQuery = "Simulação de teste.";
224
+ debugLog("Modo teste configurado");
225
  } else {
226
  formData.append('mode', 'real');
227
  originalUserQuery = document.getElementById('solicitacao_usuario').value;
228
  formData.append('solicitacao', originalUserQuery);
229
  attachedFiles.forEach(file => { formData.append('files', file); });
230
+ debugLog(`Modo real configurado. Query: ${originalUserQuery.substring(0, 100)}...`);
231
  }
232
+
233
  try {
234
+ debugLog("=== INICIANDO FETCH ===");
235
  const response = await fetch('/process', { method: 'POST', body: formData });
236
  if (!response.ok || !response.body) throw new Error(`Erro na resposta do servidor: ${response.statusText}`);
237
 
238
+ debugLog("=== FETCH REALIZADO COM SUCESSO, INICIANDO STREAM ===");
239
  const reader = response.body.getReader();
240
  const decoder = new TextDecoder();
241
+ let buffer = ''; // Buffer para dados incompletos
242
+
243
  while (true) {
244
  const { done, value } = await reader.read();
245
+ if (done) {
246
+ debugLog("=== STREAM FINALIZADO ===");
247
+ break;
248
+ }
249
+
250
  const chunk = decoder.decode(value, { stream: true });
251
+ buffer += chunk;
252
+ debugLog(`Chunk recebido: ${chunk.length} chars`);
253
+
254
+ // Processa todas as linhas completas no buffer
255
+ let lines = buffer.split('\n\n');
256
+ buffer = lines.pop(); // Mantém a última linha (possivelmente incompleta) no buffer
257
+
258
  lines.forEach(line => {
259
  if (line.startsWith('data: ')) {
260
+ const jsonData = line.substring(6).trim();
261
+ if (jsonData) {
262
+ debugLog(`JSON recebido: ${jsonData.substring(0, 200)}...`);
263
  try {
264
  const data = JSON.parse(jsonData);
265
+ debugLog(`Dados processados: ${JSON.stringify({progress: data.progress, message: data.message, hasPartialResult: !!data.partial_result, error: data.error})}`);
266
  processStreamData(data, false);
267
+ } catch (e) {
268
+ console.error("Erro ao parsear JSON do stream:", jsonData.substring(0, 200));
269
+ console.error("Erro:", e);
270
+ }
271
  }
272
  }
273
  });
274
  }
275
+
276
+ // Processa qualquer dado restante no buffer
277
+ if (buffer.trim()) {
278
+ debugLog(`Buffer final: ${buffer}`);
279
+ }
280
+
281
  } catch (error) {
282
+ debugLog(`ERRO NO FETCH: ${error.message}`);
283
  showError('A conexão com o servidor falhou.');
284
  loader.style.display = 'none';
285
  console.error("Fetch Error:", error);
 
288
 
289
  // --- Lógica do Botão de Merge ---
290
  mergeBtn.addEventListener('click', async function() {
291
+ debugLog("=== MERGE BUTTON CLICADO ===");
292
  loaderMessage.textContent = 'Processando o merge dos textos...';
293
  progressBar.style.width = '0%';
294
  loader.style.display = 'flex';
295
  this.style.display = 'none';
296
+
297
  // Sempre pega o texto bruto para o merge
298
  const payload = {
299
  solicitacao_usuario: originalUserQuery,
 
301
  sonnet_text: rawTexts['sonnet-output'] || '',
302
  gemini_text: rawTexts['gemini-output'] || '',
303
  };
304
+ debugLog(`Payload do merge: ${JSON.stringify({...payload, grok_length: payload.grok_text.length, sonnet_length: payload.sonnet_text.length, gemini_length: payload.gemini_text.length})}`);
305
 
306
  try {
307
  const response = await fetch('/merge', {
 
331
  });
332
  }
333
  } catch (error) {
334
+ debugLog(`ERRO NO MERGE: ${error.message}`);
335
  showError("A conexão falhou ao tentar processar o merge.");
336
  loader.style.display = 'none';
337
  }
 
339
 
340
  // --- Função de Processamento de Stream (ATUALIZADA) ---
341
  function processStreamData(data, isMerge) {
342
+ debugLog(`=== PROCESSANDO STREAM DATA ===`);
343
+ debugLog(`Progress: ${data.progress}, Message: ${data.message}`);
344
+ debugLog(`Has partial_result: ${!!data.partial_result}`);
345
+ debugLog(`Has final_result: ${!!data.final_result}`);
346
+ debugLog(`Has error: ${!!data.error}`);
347
+ debugLog(`Is merge: ${isMerge}`);
348
+
349
  if (data.error) {
350
+ debugLog(`Erro recebido: ${data.error}`);
351
  showError(data.error);
352
  loader.style.display = 'none';
353
  return;
354
  }
355
+
356
+ if (data.progress !== undefined) {
357
+ loaderMessage.textContent = data.message || 'Processando...';
358
+ progressBar.style.width = data.progress + '%';
359
+ debugLog(`Progress atualizado: ${data.progress}%`);
360
+ }
361
 
362
  const processContent = (targetId, content, wordCount = null) => {
363
+ debugLog(`Processando conteúdo para: ${targetId}`);
364
+ debugLog(`Tamanho do conteúdo: ${content.length} chars`);
365
+
366
  const targetBox = document.getElementById(targetId);
367
+ if (!targetBox) {
368
+ debugLog(`ERRO: Box não encontrado: ${targetId}`);
369
+ return;
370
+ }
371
 
372
  // Armazena o texto bruto e exibe na tela
373
  rawTexts[targetId] = content;
374
+ targetBox.innerText = content; // Exibe texto bruto
375
+ debugLog(`Conteúdo armazenado e exibido para: ${targetId}`);
376
+
377
  if (targetId === 'final-output') {
378
  const finalTitle = document.getElementById('final-result-title');
379
  finalTitle.textContent = `Texto Final`;
 
381
  finalTitle.textContent += ` (${wordCount} palavras)`;
382
  }
383
  finalResultContainer.style.display = 'block';
384
+ debugLog("Final result container exibido");
385
  } else {
386
+ resultsContainer.style.display = 'flex';
387
+ debugLog("Results container exibido");
388
  }
389
  };
390
 
391
  if (isMerge && data.final_result) {
392
+ debugLog("Processando final result do merge");
393
  processContent('final-output', data.final_result.content, data.final_result.word_count);
394
  } else if (data.partial_result) {
395
+ debugLog(`Processando partial result para: ${data.partial_result.id}`);
396
  processContent(data.partial_result.id, data.partial_result.content);
397
  }
398
 
399
  if (data.done) {
400
+ debugLog("=== PROCESSAMENTO CONCLUÍDO ===");
401
  setTimeout(() => {
402
  loader.style.display = 'none';
403
  if (data.mode === 'atomic' && !isMerge) {
404
  mergeBtn.style.display = 'block';
405
+ debugLog("Merge button exibido para modo atomic");
406
  }
407
  }, 500);
408
  }
 
410
 
411
  // --- Funções de Utilitários (ATUALIZADAS) ---
412
  function showError(message) {
413
+ debugLog(`Exibindo erro: ${message}`);
414
  errorContainer.innerHTML = `<div class="error-box"><strong>Erro:</strong> ${message}<span class="close-btn-error" onclick="this.parentElement.style.display='none';" title="Fechar">&times;</span></div>`;
415
  }
416
 
417
  function copyToClipboard(elementId) {
418
+ debugLog(`Copiando conteúdo de: ${elementId}`);
419
  // Sempre copia o texto bruto original do objeto rawTexts
420
  const textToCopy = rawTexts[elementId];
421
  if (textToCopy !== undefined) {
422
+ navigator.clipboard.writeText(textToCopy).then(() => {
423
+ alert('Texto copiado!');
424
+ debugLog('Texto copiado com sucesso');
425
  });
426
  } else {
427
+ debugLog(`ERRO: Texto não encontrado para ${elementId}`);
428
  alert('Nenhum texto para copiar.');
429
  }
430
  }
431
 
432
  // NOVA FUNÇÃO para converter para Markdown sob demanda
433
  async function convertToMarkdown(elementId) {
434
+ debugLog(`Convertendo markdown para: ${elementId}`);
435
  const button = event.target;
436
  button.disabled = true;
437
  button.innerText = 'Convertendo...';
438
+
439
  const rawText = rawTexts[elementId];
440
  if (rawText === undefined) {
441
+ debugLog(`ERRO: Texto bruto não encontrado para ${elementId}`);
442
  showError('Não há texto para converter.');
443
  button.innerText = 'Converter para MD';
444
  button.disabled = false;
 
451
  headers: { 'Content-Type': 'application/json' },
452
  body: JSON.stringify({ text: rawText })
453
  });
 
454
  if (!response.ok) throw new Error('Falha na conversão');
455
 
456
  const data = await response.json();
457
  const targetBox = document.getElementById(elementId);
458
  targetBox.innerHTML = data.html; // Insere o HTML convertido
459
  button.innerText = 'Convertido'; // Mantém o botão desabilitado para indicar sucesso
460
+ debugLog(`Markdown convertido com sucesso para ${elementId}`);
461
  } catch (error) {
462
  showError('Não foi possível converter o texto.');
463
  console.error('Conversion error:', error);
464
+ debugLog(`Erro na conversão: ${error.message}`);
465
  button.innerText = 'Converter para MD'; // Reabilita em caso de erro
466
  button.disabled = false;
467
  }
468
  }
469
  </script>
470
  </body>
471
+ </html>