Código HTML para editor em JSON formatado (Incompleto)
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Editor de Texto Avançado com Múltiplas Abas</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: #f5f5f7;
color: #333;
min-height: 100vh;
}
.container {
max-width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
}
/* Header */
header {
background: #ffffff;
padding: 15px 25px;
border-bottom: 1px solid #e0e0e0;
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
.logo {
display: flex;
align-items: center;
gap: 15px;
}
h1 {
font-size: 1.8rem;
background: linear-gradient(to right, #0066cc, #0099ff);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.header-controls {
display: flex;
gap: 15px;
}
/* Barra de ferramentas */
.toolbar {
background: #ffffff;
padding: 15px 25px;
border-bottom: 1px solid #e0e0e0;
display: flex;
flex-wrap: wrap;
gap: 10px;
flex-shrink: 0;
}
.toolbar-group {
display: flex;
align-items: center;
gap: 8px;
padding-right: 15px;
border-right: 1px solid #e0e0e0;
}
.toolbar-group:last-child {
border-right: none;
}
.toolbar-btn {
background: #f0f0f0;
border: none;
border-radius: 6px;
color: #333;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
}
.toolbar-btn:hover {
background: #e0e0e0;
transform: translateY(-2px);
}
.toolbar-btn.active {
background: #d0d0d0;
color: #0066cc;
}
select.toolbar-btn {
width: auto;
padding: 0 10px;
min-width: 120px;
}
/* Abas e páginas */
.tabs-container {
background: #ffffff;
padding: 0 25px;
display: flex;
align-items: center;
border-bottom: 1px solid #e0e0e0;
flex-shrink: 0;
overflow-x: auto;
}
.tabs {
display: flex;
flex-grow: 1;
overflow-x: auto;
}
.tab {
padding: 12px 20px;
background: #f8f8f8;
border-right: 1px solid #e0e0e0;
cursor: pointer;
white-space: nowrap;
display: flex;
align-items: center;
gap: 8px;
min-width: 120px;
position: relative;
}
.tab.active {
background: #ffffff;
border-bottom: 2px solid #0066cc;
}
.tab:hover {
background: #f0f0f0;
}
.tab-close {
margin-left: auto;
opacity: 0.7;
font-size: 0.8rem;
}
.tab-close:hover {
opacity: 1;
color: #cc0000;
}
.add-tab {
padding: 12px 15px;
background: #f0f0f0;
border: none;
color: #333;
cursor: pointer;
font-size: 1.2rem;
min-width: 50px;
display: flex;
align-items: center;
justify-content: center;
}
.add-tab:hover {
background: #e0e0e0;
}
/* Área principal do editor */
.main-content {
display: flex;
flex-grow: 1;
overflow: hidden;
}
/* Painel de páginas */
.pages-panel {
width: 220px;
background: #ffffff;
border-right: 1px solid #e0e0e0;
padding: 20px;
overflow-y: auto;
flex-shrink: 0;
}
.pages-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.pages-header h3 {
color: #0066cc;
}
.add-page {
background: #f0f0f0;
border: none;
border-radius: 50%;
width: 30px;
height: 30px;
color: #333;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.add-page:hover {
background: #e0e0e0;
}
.pages-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.page-item {
padding: 12px 15px;
background: #f8f8f8;
border-radius: 8px;
cursor: pointer;
display: flex;
align-items: center;
gap: 10px;
transition: all 0.2s;
}
.page-item.active {
background: #e6f2ff;
border-left: 3px solid #0066cc;
}
.page-item:hover {
background: #f0f0f0;
}
.page-number {
background: #ffffff;
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
border: 1px solid #e0e0e0;
}
.page-title {
flex-grow: 1;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.page-actions {
display: flex;
gap: 5px;
}
.page-action-btn {
background: none;
border: none;
color: #666;
cursor: pointer;
font-size: 0.8rem;
}
.page-action-btn:hover {
color: #333;
}
/* Área do editor */
.editor-area {
flex-grow: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.editor-container {
flex-grow: 1;
padding: 25px;
overflow-y: auto;
background: #f9f9f9;
}
.editor {
background: #ffffff;
border: 1px solid #ddd;
border-radius: 8px;
padding: 30px;
min-height: 100%;
outline: none;
font-size: 1rem;
line-height: 1.6;
overflow-y: auto;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
.editor:focus {
border-color: #0066cc;
box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.1);
}
/* Painel de propriedades */
.properties-panel {
width: 280px;
background: #ffffff;
border-left: 1px solid #e0e0e0;
padding: 20px;
overflow-y: auto;
flex-shrink: 0;
}
.properties-panel h3 {
color: #0066cc;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #e0e0e0;
}
.property-group {
margin-bottom: 25px;
}
.property-group h4 {
color: #333;
margin-bottom: 10px;
font-size: 0.95rem;
}
.property-item {
margin-bottom: 15px;
}
.property-label {
display: block;
color: #666;
margin-bottom: 5px;
font-size: 0.9rem;
}
.property-input {
width: 100%;
padding: 10px;
background: #f8f8f8;
border: 1px solid #ddd;
border-radius: 6px;
color: #333;
}
.property-input:focus {
outline: none;
border-color: #0066cc;
}
.color-picker {
display: flex;
gap: 10px;
align-items: center;
}
.color-preview {
width: 30px;
height: 30px;
border-radius: 6px;
border: 2px solid #e0e0e0;
}
.color-input {
flex-grow: 1;
}
.toggle-switch {
display: flex;
align-items: center;
gap: 10px;
}
.toggle {
width: 50px;
height: 26px;
background: #ddd;
border-radius: 13px;
position: relative;
cursor: pointer;
}
.toggle::after {
content: '';
position: absolute;
width: 20px;
height: 20px;
background: #ffffff;
border-radius: 50%;
top: 3px;
left: 3px;
transition: all 0.3s;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.toggle.active {
background: #0066cc;
}
.toggle.active::after {
left: 27px;
}
/* Botões principais */
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
transition: all 0.2s;
}
.btn-primary {
background: #0066cc;
color: white;
}
.btn-primary:hover {
background: #0052a3;
transform: translateY(-2px);
}
.btn-secondary {
background: #f0f0f0;
color: #333;
}
.btn-secondary:hover {
background: #e0e0e0;
transform: translateY(-2px);
}
.btn-success {
background: #28a745;
color: white;
}
.btn-success:hover {
background: #218838;
transform: translateY(-2px);
}
.btn-danger {
background: #dc3545;
color: white;
}
.btn-danger:hover {
background: #c82333;
transform: translateY(-2px);
}
.btn-print {
background: #6c757d;
color: white;
}
.btn-print:hover {
background: #5a6268;
transform: translateY(-2px);
}
/* Modal */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
align-items: center;
justify-content: center;
}
.modal.active {
display: flex;
}
.modal-content {
background: #ffffff;
border-radius: 12px;
width: 500px;
max-width: 90%;
max-height: 90%;
overflow-y: auto;
border: 1px solid #ddd;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}
.modal-header {
padding: 20px;
border-bottom: 1px solid #e0e0e0;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h3 {
color: #0066cc;
}
.modal-close {
background: none;
border: none;
color: #666;
font-size: 1.5rem;
cursor: pointer;
}
.modal-body {
padding: 20px;
}
.modal-footer {
padding: 20px;
border-top: 1px solid #e0e0e0;
display: flex;
justify-content: flex-end;
gap: 10px;
}
/* Status bar */
.status-bar {
background: #ffffff;
padding: 10px 25px;
border-top: 1px solid #e0e0e0;
display: flex;
justify-content: space-between;
font-size: 0.9rem;
color: #666;
flex-shrink: 0;
}
.word-count, .save-status {
display: flex;
align-items: center;
gap: 10px;
}
/* Responsividade */
@media (max-width: 1200px) {
.properties-panel {
display: none;
}
}
@media (max-width: 900px) {
.pages-panel {
display: none;
}
.toolbar {
padding: 10px;
}
.toolbar-group {
padding-right: 10px;
}
}
/* Estilos para o conteúdo editável */
.editor h1, .editor h2, .editor h3, .editor h4 {
color: #0066cc;
margin: 20px 0 10px;
}
.editor p {
margin: 10px 0;
color: #333;
}
.editor ul, .editor ol {
margin: 10px 0 10px 25px;
color: #333;
}
.editor blockquote {
border-left: 4px solid #6c757d;
margin: 15px 0;
padding-left: 15px;
color: #666;
font-style: italic;
}
.editor code {
background: #f8f9fa;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', monospace;
color: #d63384;
border: 1px solid #e9ecef;
}
.editor pre {
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
margin: 15px 0;
border: 1px solid #e9ecef;
}
.editor pre code {
background: none;
border: none;
padding: 0;
}
.editor table {
border-collapse: collapse;
width: 100%;
margin: 15px 0;
}
.editor th, .editor td {
border: 1px solid #dee2e6;
padding: 10px;
text-align: left;
}
.editor th {
background: #f8f9fa;
font-weight: 600;
}
.editor a {
color: #0066cc;
text-decoration: none;
}
.editor a:hover {
text-decoration: underline;
}
/* Estilos para impressão */
@media print {
header, .toolbar, .tabs-container, .pages-panel,
.properties-panel, .status-bar, .modal {
display: none !important;
}
.container {
height: auto;
}
.main-content {
display: block;
}
.editor-container {
padding: 0;
background: white;
}
.editor {
border: none;
box-shadow: none;
padding: 20px;
min-height: auto;
}
body, .editor {
background: white !important;
color: black !important;
}
.editor h1, .editor h2, .editor h3, .editor h4 {
color: black !important;
}
.editor a {
color: black !important;
text-decoration: underline;
}
.editor table, .editor th, .editor td {
border-color: black !important;
}
}
</style>
</head>
<body>
<div class="container">
<!-- Header -->
<header>
<div class="logo">
<i class="fas fa-file-alt" style="color: #0066cc; font-size: 1.8rem;"></i>
<h1>Editor Avançado</h1>
</div>
<div class="header-controls">
<button class="btn btn-secondary" onclick="newDocument()">
<i class="fas fa-file"></i> Novo
</button>
<button class="btn btn-primary" onclick="saveDocument()">
<i class="fas fa-save"></i> Salvar
</button>
<button class="btn btn-success" onclick="loadDocument()">
<i class="fas fa-folder-open"></i> Carregar
</button>
<button class="btn btn-print" onclick="printDocument()">
<i class="fas fa-print"></i> Imprimir
</button>
</div>
</header>
<!-- Barra de ferramentas -->
<div class="toolbar">
<div class="toolbar-group">
<button class="toolbar-btn" onclick="formatText('bold')" title="Negrito">
<i class="fas fa-bold"></i>
</button>
<button class="toolbar-btn" onclick="formatText('italic')" title="Itálico">
<i class="fas fa-italic"></i>
</button>
<button class="toolbar-btn" onclick="formatText('underline')" title="Sublinhado">
<i class="fas fa-underline"></i>
</button>
</div>
<div class="toolbar-group">
<button class="toolbar-btn" onclick="formatText('justifyLeft')" title="Alinhar à esquerda">
<i class="fas fa-align-left"></i>
</button>
<button class="toolbar-btn" onclick="formatText('justifyCenter')" title="Centralizar">
<i class="fas fa-align-center"></i>
</button>
<button class="toolbar-btn" onclick="formatText('justifyRight')" title="Alinhar à direita">
<i class="fas fa-align-right"></i>
</button>
<button class="toolbar-btn" onclick="formatText('justifyFull')" title="Justificar">
<i class="fas fa-align-justify"></i>
</button>
</div>
<div class="toolbar-group">
<select class="toolbar-btn" id="fontSize" onchange="changeFontSize(this.value)" title="Tamanho da fonte">
<option value="1">8pt</option>
<option value="2">10pt</option>
<option value="3" selected>12pt</option>
<option value="4">14pt</option>
<option value="5">18pt</option>
<option value="6">24pt</option>
<option value="7">36pt</option>
</select>
<select class="toolbar-btn" id="fontFamily" onchange="changeFontFamily(this.value)" title="Fonte">
<option value="Arial">Arial</option>
<option value="'Segoe UI'">Segoe UI</option>
<option value="Georgia">Georgia</option>
<option value="'Times New Roman'">Times New Roman</option>
<option value="'Courier New'">Courier New</option>
<option value="Verdana">Verdana</option>
</select>
<div class="color-picker">
<div class="color-preview" id="colorPreview" style="background: #333333;"></div>
<input type="color" class="toolbar-btn color-input" id="textColor"
value="#333333" onchange="changeTextColor(this.value)" title="Cor do texto">
</div>
<div class="color-picker">
<div class="color-preview" id="bgColorPreview" style="background: #ffffff;"></div>
<input type="color" class="toolbar-btn color-input" id="bgColor"
value="#ffffff" onchange="changeBgColor(this.value)" title="Cor de fundo">
</div>
</div>
<div class="toolbar-group">
<button class="toolbar-btn" onclick="formatText('insertUnorderedList')" title="Lista não ordenada">
<i class="fas fa-list-ul"></i>
</button>
<button class="toolbar-btn" onclick="formatText('insertOrderedList')" title="Lista ordenada">
<i class="fas fa-list-ol"></i>
</button>
<button class="toolbar-btn" onclick="insertLink()" title="Inserir link">
<i class="fas fa-link"></i>
</button>
<button class="toolbar-btn" onclick="insertImage()" title="Inserir imagem">
<i class="fas fa-image"></i>
</button>
<button class="toolbar-btn" onclick="formatText('formatBlock', '<blockquote>')" title="Citação">
<i class="fas fa-quote-right"></i>
</button>
<button class="toolbar-btn" onclick="insertTable()" title="Inserir tabela">
<i class="fas fa-table"></i>
</button>
</div>
<div class="toolbar-group">
<button class="toolbar-btn" onclick="formatText('undo')" title="Desfazer">
<i class="fas fa-undo"></i>
</button>
<button class="toolbar-btn" onclick="formatText('redo')" title="Refazer">
<i class="fas fa-redo"></i>
</button>
<button class="toolbar-btn" onclick="clearFormatting()" title="Limpar formatação">
<i class="fas fa-eraser"></i>
</button>
</div>
</div>
<!-- Abas -->
<div class="tabs-container">
<div class="tabs" id="tabs">
<!-- Abas serão criadas dinamicamente aqui -->
<div class="tab active" data-tab-id="1">
<i class="fas fa-file-alt"></i>
<span>Documento 1</span>
<span class="tab-close" onclick="closeTab(event, 1)">×</span>
</div>
</div>
<button class="add-tab" onclick="addTab()">
<i class="fas fa-plus"></i>
</button>
</div>
<!-- Área principal -->
<div class="main-content">
<!-- Painel de páginas -->
<div class="pages-panel">
<div class="pages-header">
<h3>Páginas</h3>
<button class="add-page" onclick="addPage()">
<i class="fas fa-plus"></i>
</button>
</div>
<div class="pages-list" id="pagesList">
<!-- Páginas serão criadas dinamicamente aqui -->
<div class="page-item active" data-page-id="1">
<div class="page-number">1</div>
<div class="page-title">Página 1</div>
<div class="page-actions">
<button class="page-action-btn" onclick="renamePage(event, 1)" title="Renomear">
<i class="fas fa-edit"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Editor -->
<div class="editor-area">
<div class="editor-container">
<div class="editor" id="editor" contenteditable="true" data-page-id="1">
<h1>Bem-vindo ao Editor Avançado</h1>
<p>Este é um editor de texto completo com formatação, múltiplas abas e páginas. Você pode:</p>
<ul>
<li>Criar múltiplas abas para diferentes documentos</li>
<li>Adicionar várias páginas em cada documento</li>
<li>Formatar texto com negrito, itálico, sublinhado</li>
<li>Alterar fontes, cores e tamanhos</li>
<li>Inserir listas, tabelas, links e imagens</li>
<li>Salvar tudo em formato JSON</li>
<li>Imprimir seus documentos</li>
</ul>
<p>Comece editando este texto ou crie um novo documento!</p>
<h2>Dica de Impressão</h2>
<p>Use o botão <strong>Imprimir</strong> no cabeçalho para imprimir o documento atual. O editor removerá automaticamente todos os controles de interface durante a impressão.</p>
</div>
</div>
</div>
<!-- Painel de propriedades -->
<div class="properties-panel">
<h3>Propriedades</h3>
<div class="property-group">
<h4>Documento</h4>
<div class="property-item">
<label class="property-label">Nome do Documento</label>
<input type="text" class="property-input" id="docName" value="Documento 1" onchange="updateDocumentName(this.value)">
</div>
<div class="property-item">
<label class="property-label">Autor</label>
<input type="text" class="property-input" id="docAuthor" value="" onchange="updateDocumentAuthor(this.value)" placeholder="Seu nome">
</div>
</div>
<div class="property-group">
<h4>Página Atual</h4>
<div class="property-item">
<label class="property-label">Título da Página</label>
<input type="text" class="property-input" id="pageTitle" value="Página 1" onchange="updatePageTitle(this.value)">
</div>
<div class="property-item">
<label class="property-label">Cor de Fundo</label>
<div class="color-picker">
<div class="color-preview" id="pageBgPreview" style="background: #ffffff;"></div>
<input type="color" class="property-input color-input" id="pageBgColor"
value="#ffffff" onchange="updatePageBgColor(this.value)">
</div>
</div>
</div>
<div class="property-group">
<h4>Configurações</h4>
<div class="property-item">
<div class="toggle-switch">
<div class="toggle" id="autoSaveToggle" onclick="toggleAutoSave()"></div>
<span class="property-label">Salvamento Automático</span>
</div>
</div>
<div class="property-item">
<div class="toggle-switch">
<div class="toggle active" id="spellCheckToggle" onclick="toggleSpellCheck()"></div>
<span class="property-label">Verificação Ortográfica</span>
</div>
</div>
</div>
<div class="property-group">
<h4>Estatísticas</h4>
<div class="property-item">
<label class="property-label">Palavras na Página</label>
<input type="text" class="property-input" id="pageWordCount" value="0" readonly>
</div>
<div class="property-item">
<label class="property-label">Caracteres na Página</label>
<input type="text" class="property-input" id="pageCharCount" value="0" readonly>
</div>
</div>
<button class="btn btn-primary" style="width: 100%; margin-top: 20px;" onclick="exportDocument()">
<i class="fas fa-download"></i> Exportar Documento
</button>
<button class="btn btn-print" style="width: 100%; margin-top: 10px;" onclick="printDocument()">
<i class="fas fa-print"></i> Imprimir Documento
</button>
</div>
</div>
<!-- Barra de status -->
<div class="status-bar">
<div class="word-count">
<span>Palavras: <span id="totalWords">0</span></span>
<span>Caracteres: <span id="totalChars">0</span></span>
</div>
<div class="save-status">
<span id="saveStatus">Pronto</span>
<span id="lastSave">Nunca salvo</span>
</div>
</div>
</div>
<!-- Modal para inserção de link -->
<div class="modal" id="linkModal">
<div class="modal-content">
<div class="modal-header">
<h3>Inserir Link</h3>
<button class="modal-close" onclick="closeModal('linkModal')">×</button>
</div>
<div class="modal-body">
<div class="property-item">
<label class="property-label">URL</label>
<input type="text" class="property-input" id="linkUrl" placeholder="https://exemplo.com">
</div>
<div class="property-item">
<label class="property-label">Texto do Link</label>
<input type="text" class="property-input" id="linkText" placeholder="Texto do link">
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('linkModal')">Cancelar</button>
<button class="btn btn-primary" onclick="insertLinkConfirm()">Inserir</button>
</div>
</div>
</div>
<!-- Modal para inserção de imagem -->
<div class="modal" id="imageModal">
<div class="modal-content">
<div class="modal-header">
<h3>Inserir Imagem</h3>
<button class="modal-close" onclick="closeModal('imageModal')">×</button>
</div>
<div class="modal-body">
<div class="property-item">
<label class="property-label">URL da Imagem</label>
<input type="text" class="property-input" id="imageUrl" placeholder="https://exemplo.com/imagem.jpg">
</div>
<div class="property-item">
<label class="property-label">Descrição (alt)</label>
<input type="text" class="property-input" id="imageAlt" placeholder="Descrição da imagem">
</div>
<div class="property-item">
<label class="property-label">Largura (px)</label>
<input type="number" class="property-input" id="imageWidth" placeholder="Auto">
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('imageModal')">Cancelar</button>
<button class="btn btn-primary" onclick="insertImageConfirm()">Inserir</button>
</div>
</div>
</div>
<!-- Modal para inserção de tabela -->
<div class="modal" id="tableModal">
<div class="modal-content">
<div class="modal-header">
<h3>Inserir Tabela</h3>
<button class="modal-close" onclick="closeModal('tableModal')">×</button>
</div>
<div class="modal-body">
<div class="property-item">
<label class="property-label">Linhas</label>
<input type="number" class="property-input" id="tableRows" value="3" min="1" max="20">
</div>
<div class="property-item">
<label class="property-label">Colunas</label>
<input type="number" class="property-input" id="tableCols" value="3" min="1" max="10">
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('tableModal')">Cancelar</button>
<button class="btn btn-primary" onclick="insertTableConfirm()">Inserir</button>
</div>
</div>
</div>
<!-- Modal para carregar documento -->
<div class="modal" id="loadModal">
<div class="modal-content">
<div class="modal-header">
<h3>Carregar Documento</h3>
<button class="modal-close" onclick="closeModal('loadModal')">×</button>
</div>
<div class="modal-body">
<div class="property-item">
<label class="property-label">Escolher arquivo JSON</label>
<input type="file" class="property-input" id="loadFile" accept=".json">
</div>
<p style="margin-top: 20px; color: #666; font-size: 0.9rem;">
Ou selecione um documento salvo localmente:
</p>
<div id="savedDocuments" style="margin-top: 10px;">
<!-- Documentos salvos aparecerão aqui -->
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('loadModal')">Cancelar</button>
</div>
</div>
</div>
<!-- Modal de pré-visualização de impressão -->
<div class="modal" id="printPreviewModal">
<div class="modal-content" style="width: 90%; max-width: 800px;">
<div class="modal-header">
<h3>Pré-visualização de Impressão</h3>
<button class="modal-close" onclick="closeModal('printPreviewModal')">×</button>
</div>
<div class="modal-body">
<div id="printPreviewContent" style="background: white; padding: 20px; border: 1px solid #ddd; border-radius: 8px;">
<!-- Conteúdo da pré-visualização será inserido aqui -->
</div>
<p style="margin-top: 20px; color: #666; font-size: 0.9rem; text-align: center;">
Esta é uma pré-visualização do que será impresso. Use o botão abaixo para abrir a caixa de diálogo de impressão.
</p>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('printPreviewModal')">Cancelar</button>
<button class="btn btn-primary" onclick="executePrint()">
<i class="fas fa-print"></i> Abrir Diálogo de Impressão
</button>
</div>
</div>
</div>
<script>
// Estado global da aplicação
let state = {
documents: {},
currentTabId: 1,
currentPageId: 1,
nextTabId: 2,
nextPageId: 2,
autoSave: false,
spellCheck: true,
lastSaveTime: null
};
// Documento padrão
state.documents[1] = {
id: 1,
name: "Documento 1",
author: "",
created: new Date().toISOString(),
modified: new Date().toISOString(),
pages: {
1: {
id: 1,
title: "Página 1",
content: document.getElementById('editor').innerHTML,
backgroundColor: "#ffffff",
order: 1
}
},
pageOrder: [1],
activePage: 1
};
// Inicialização
document.addEventListener('DOMContentLoaded', function() {
// Carregar estado salvo do localStorage
loadStateFromStorage();
// Atualizar contadores
updateWordCount();
// Configurar salvamento automático se ativado
if (state.autoSave) {
document.getElementById('autoSaveToggle').classList.add('active');
startAutoSave();
}
if (state.spellCheck) {
document.getElementById('spellCheckToggle').classList.add('active');
document.getElementById('editor').setAttribute('spellcheck', 'true');
}
// Configurar eventos do editor
document.getElementById('editor').addEventListener('input', function() {
updateWordCount();
savePageContent();
});
// Atualizar a cada segundo para pegar mudanças de seleção
setInterval(updateWordCount, 1000);
// Atualizar status de salvamento
updateSaveStatus();
// Carregar documentos salvos
loadSavedDocumentsList();
// Configurar evento de carregamento de arquivo
document.getElementById('loadFile').addEventListener('change', loadDocumentFromFile);
});
// Funções de formatação
function formatText(command, value = null) {
document.execCommand(command, false, value);
document.getElementById('editor').focus();
}
function changeFontSize(size) {
document.execCommand('fontSize', false, size);
// O comando fontSize do execCommand não é perfeito, então ajustamos
const fontSizes = {
1: '8px', 2: '10px', 3: '12px',
4: '14px', 5: '18px', 6: '24px', 7: '36px'
};
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
const span = document.createElement('span');
span.style.fontSize = fontSizes[size];
range.surroundContents(span);
}
}
function changeFontFamily(font) {
document.execCommand('fontName', false, font);
}
function changeTextColor(color) {
document.execCommand('foreColor', false, color);
document.getElementById('colorPreview').style.backgroundColor = color;
}
function changeBgColor(color) {
document.execCommand('backColor', false, color);
document.getElementById('bgColorPreview').style.backgroundColor = color;
}
function clearFormatting() {
document.execCommand('removeFormat', false, null);
document.execCommand('unlink', false, null);
}
// Modal de link
function insertLink() {
const selection = window.getSelection();
const selectedText = selection.toString();
if (selectedText) {
document.getElementById('linkText').value = selectedText;
}
openModal('linkModal');
}
function insertLinkConfirm() {
const url = document.getElementById('linkUrl').value;
const text = document.getElementById('linkText').value || url;
if (url) {
const html = `<a href="${url}" target="_blank">${text}</a>`;
document.execCommand('insertHTML', false, html);
}
closeModal('linkModal');
document.getElementById('linkUrl').value = '';
document.getElementById('linkText').value = '';
}
// Modal de imagem
function insertImage() {
openModal('imageModal');
}
function insertImageConfirm() {
const url = document.getElementById('imageUrl').value;
const alt = document.getElementById('imageAlt').value;
const width = document.getElementById('imageWidth').value;
if (url) {
let imgHtml = `<img src="${url}" alt="${alt}"`;
if (width) {
imgHtml += ` style="width: ${width}px;"`;
}
imgHtml += '>';
document.execCommand('insertHTML', false, imgHtml);
}
closeModal('imageModal');
document.getElementById('imageUrl').value = '';
document.getElementById('imageAlt').value = '';
document.getElementById('imageWidth').value = '';
}
// Modal de tabela
function insertTable() {
openModal('tableModal');
}
function insertTableConfirm() {
const rows = parseInt(document.getElementById('tableRows').value);
const cols = parseInt(document.getElementById('tableCols').value);
let tableHtml = '<table style="width: 100%; border-collapse: collapse;"><thead><tr>';
// Cabeçalho
for (let i = 0; i < cols; i++) {
tableHtml += `<th style="border: 1px solid #dee2e6; padding: 8px; background: #f8f9fa;">Cabeçalho ${i+1}</th>`;
}
tableHtml += '</tr></thead><tbody>';
// Linhas
for (let i = 0; i < rows; i++) {
tableHtml += '<tr>';
for (let j = 0; j < cols; j++) {
tableHtml += `<td style="border: 1px solid #dee2e6; padding: 8px;">Célula ${i+1},${j+1}</td>`;
}
tableHtml += '</tr>';
}
tableHtml += '</tbody></table>';
document.execCommand('insertHTML', false, tableHtml);
closeModal('tableModal');
}
// Funções de modal
function openModal(modalId) {
document.getElementById(modalId).classList.add('active');
}
function closeModal(modalId) {
document.getElementById(modalId).classList.remove('active');
}
// Gerenciamento de abas
function addTab() {
const tabId = state.nextTabId++;
const docId = tabId;
// Criar novo documento
state.documents[docId] = {
id: docId,
name: `Documento ${tabId}`,
author: "",
created: new Date().toISOString(),
modified: new Date().toISOString(),
pages: {
1: {
id: 1,
title: "Página 1",
content: "<h1>Novo Documento</h1><p>Comece a escrever aqui...</p>",
backgroundColor: "#ffffff",
order: 1
}
},
pageOrder: [1],
activePage: 1
};
// Atualizar a interface
updateTabsUI();
switchTab(tabId);
}
function switchTab(tabId) {
state.currentTabId = tabId;
// Atualizar abas ativas
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.toggle('active', parseInt(tab.dataset.tabId) === tabId);
});
// Carregar documento
const doc = state.documents[tabId];
state.currentPageId = doc.activePage;
// Atualizar propriedades do documento
document.getElementById('docName').value = doc.name;
document.getElementById('docAuthor').value = doc.author || '';
// Atualizar lista de páginas
updatePagesUI();
// Carregar página ativa
switchPage(doc.activePage);
}
function closeTab(event, tabId) {
event.stopPropagation();
if (Object.keys(state.documents).length <= 1) {
alert("Não é possível fechar a última aba. Crie uma nova aba primeiro.");
return;
}
// Remover documento
delete state.documents[tabId];
// Se a aba fechada era a ativa, mudar para outra
if (state.currentTabId === tabId) {
const remainingTabIds = Object.keys(state.documents);
if (remainingTabIds.length > 0) {
switchTab(parseInt(remainingTabIds[0]));
}
}
updateTabsUI();
}
function updateTabsUI() {
const tabsContainer = document.getElementById('tabs');
tabsContainer.innerHTML = '';
Object.keys(state.documents).forEach(docId => {
const doc = state.documents[docId];
const isActive = parseInt(docId) === state.currentTabId;
const tab = document.createElement('div');
tab.className = `tab ${isActive ? 'active' : ''}`;
tab.dataset.tabId = docId;
tab.onclick = () => switchTab(parseInt(docId));
tab.innerHTML = `
<i class="fas fa-file-alt"></i>
<span>${doc.name}</span>
<span class="tab-close" onclick="closeTab(event, ${docId})">×</span>
`;
tabsContainer.appendChild(tab);
});
}
// Gerenciamento de páginas
function addPage() {
const pageId = state.nextPageId++;
const doc = state.documents[state.currentTabId];
doc.pages[pageId] = {
id: pageId,
title: `Página ${pageId}`,
content: "<p>Nova página...</p>",
backgroundColor: "#ffffff",
order: doc.pageOrder.length + 1
};
doc.pageOrder.push(pageId);
doc.modified = new Date().toISOString();
updatePagesUI();
switchPage(pageId);
}
function switchPage(pageId) {
state.currentPageId = pageId;
const doc = state.documents[state.currentTabId];
doc.activePage = pageId;
// Atualizar lista de páginas
document.querySelectorAll('.page-item').forEach(item => {
item.classList.toggle('active', parseInt(item.dataset.pageId) === pageId);
});
// Carregar conteúdo da página
const page = doc.pages[pageId];
const editor = document.getElementById('editor');
editor.innerHTML = page.content;
editor.dataset.pageId = pageId;
editor.style.backgroundColor = page.backgroundColor;
// Atualizar propriedades da página
document.getElementById('pageTitle').value = page.title;
document.getElementById('pageBgColor').value = page.backgroundColor;
document.getElementById('pageBgPreview').style.backgroundColor = page.backgroundColor;
updateWordCount();
}
function renamePage(event, pageId) {
event.stopPropagation();
const newTitle = prompt("Novo nome para a página:", state.documents[state.currentTabId].pages[pageId].title);
if (newTitle && newTitle.trim()) {
state.documents[state.currentTabId].pages[pageId].title = newTitle.trim();
state.documents[state.currentTabId].modified = new Date().toISOString();
updatePagesUI();
}
}
function deletePage(event, pageId) {
event.stopPropagation();
const doc = state.documents[state.currentTabId];
if (Object.keys(doc.pages).length <= 1) {
alert("Não é possível excluir a última página. Crie uma nova página primeiro.");
return;
}
if (confirm(`Tem certeza que deseja excluir "${doc.pages[pageId].title}"?`)) {
// Remover página
delete doc.pages[pageId];
doc.pageOrder = doc.pageOrder.filter(id => id !== pageId);
// Reordenar páginas restantes
let order = 1;
doc.pageOrder.forEach(id => {
doc.pages[id].order = order++;
});
// Se a página excluída era a ativa, mudar para outra
if (state.currentPageId === pageId) {
const remainingPageIds = Object.keys(doc.pages);
if (remainingPageIds.length > 0) {
switchPage(parseInt(remainingPageIds[0]));
}
}
doc.modified = new Date().toISOString();
updatePagesUI();
}
}
// Atualizações de propriedades
function updateDocumentName(name) {
state.documents[state.currentTabId].name = name;
state.documents[state.currentTabId].modified = new Date().toISOString();
updateTabsUI();
}
function updateDocumentAuthor(author) {
state.documents[state.currentTabId].author = author;
state.documents[state.currentTabId].modified = new Date().toISOString();
}
function updatePageTitle(title) {
state.documents[state.currentTabId].pages[state.currentPageId].title = title;
state.documents[state.currentTabId].modified = new Date().toISOString();
updatePagesUI();
}
function updatePageBgColor(color) {
const page = state.documents[state.currentTabId].pages[state.currentPageId];
page.backgroundColor = color;
document.getElementById('editor').style.backgroundColor = color;
document.getElementById('pageBgPreview').style.backgroundColor = color;
state.documents[state.currentTabId].modified = new Date().toISOString();
}
// Salvar conteúdo da página
function savePageContent() {
const doc = state.documents[state.currentTabId];
const page = doc.pages[state.currentPageId];
page.content = document.getElementById('editor').innerHTML;
doc.modified = new Date().toISOString();
// Salvar no localStorage se autoSave estiver ativado
if (state.autoSave) {
saveStateToStorage();
}
}
// Contagem de palavras
function updateWordCount() {
const editor = document.getElementById('editor');
const text = editor.innerText || editor.textContent;
// Remover espaços extras e contar palavras
const words = text.trim().split(/\s+/).filter(word => word.length > 0);
const wordCount = words.length;
const charCount = text.length;
// Atualizar estatísticas da página
document.getElementById('pageWordCount').value = wordCount;
document.getElementById('pageCharCount').value = charCount;
// Calcular total do documento
let totalWords = 0;
let totalChars = 0;
const doc = state.documents[state.currentTabId];
Object.values(doc.pages).forEach(page => {
const pageText = page.content.replace(/<[^>]*>/g, '');
const pageWords = pageText.trim().split(/\s+/).filter(word => word.length > 0);
totalWords += pageWords.length;
totalChars += pageText.length;
});
// Atualizar barra de status
document.getElementById('totalWords').textContent = totalWords;
document.getElementById('totalChars').textContent = totalChars;
}
// Toggles
function toggleAutoSave() {
const toggle = document.getElementById('autoSaveToggle');
state.autoSave = !state.autoSave;
if (state.autoSave) {
toggle.classList.add('active');
startAutoSave();
showNotification('Salvamento automático ativado');
} else {
toggle.classList.remove('active');
showNotification('Salvamento automático desativado');
}
saveStateToStorage();
}
function toggleSpellCheck() {
const toggle = document.getElementById('spellCheckToggle');
state.spellCheck = !state.spellCheck;
if (state.spellCheck) {
toggle.classList.add('active');
document.getElementById('editor').setAttribute('spellcheck', 'true');
showNotification('Verificação ortográfica ativada');
} else {
toggle.classList.remove('active');
document.getElementById('editor').setAttribute('spellcheck', 'false');
showNotification('Verificação ortográfica desativada');
}
saveStateToStorage();
}
// Salvamento automático
function startAutoSave() {
// Salvar a cada 30 segundos se houver mudanças
setInterval(() => {
if (state.autoSave) {
saveStateToStorage();
updateSaveStatus();
}
}, 30000);
}
function updateSaveStatus() {
const doc = state.documents[state.currentTabId];
const lastSave = state.lastSaveTime ? new Date(state.lastSaveTime).toLocaleTimeString('pt-BR') : 'Nunca';
document.getElementById('lastSave').textContent = `Salvo: ${lastSave}`;
document.getElementById('saveStatus').textContent = 'Pronto';
}
// Criar novo documento
function newDocument() {
if (confirm("Deseja criar um novo documento? As alterações não salvas podem ser perdidas.")) {
addTab();
}
}
// Salvar documento
function saveDocument() {
// Primeiro, salvar o conteúdo atual
savePageContent();
// Preparar dados para exportação
const exportData = prepareExportData();
// Criar blob e link para download
const jsonData = JSON.stringify(exportData, null, 2);
const blob = new Blob([jsonData], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${exportData.document.name || 'documento'}_${new Date().toISOString().slice(0, 10)}.json`;
document.body.appendChild(a);
a.click();
// Limpar
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 100);
// Atualizar status
state.lastSaveTime = new Date().toISOString();
updateSaveStatus();
showNotification('Documento salvo com sucesso!');
// Salvar no localStorage também
saveStateToStorage();
// Atualizar lista de documentos salvos
saveDocumentToLocalStorage(exportData);
loadSavedDocumentsList();
}
function prepareExportData() {
const doc = state.documents[state.currentTabId];
return {
version: "1.0",
exportDate: new Date().toISOString(),
application: "Editor Avançado",
document: {
id: doc.id,
name: doc.name,
author: doc.author,
created: doc.created,
modified: doc.modified,
settings: {
autoSave: state.autoSave,
spellCheck: state.spellCheck
},
pages: Object.values(doc.pages).map(page => ({
id: page.id,
title: page.title,
content: page.content,
backgroundColor: page.backgroundColor,
order: page.order
})),
pageOrder: doc.pageOrder,
activePage: doc.activePage
}
};
}
// Carregar documento
function loadDocument() {
openModal('loadModal');
}
function loadDocumentFromFile(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
try {
const data = JSON.parse(e.target.result);
importDocumentData(data);
} catch (error) {
alert('Erro ao carregar arquivo: Formato JSON inválido');
console.error(error);
}
};
reader.readAsText(file);
}
function importDocumentData(data) {
try {
// Verificar se é um documento válido
if (!data.document || !data.document.pages) {
throw new Error('Formato de documento inválido');
}
const docData = data.document;
const tabId = state.nextTabId++;
// Criar novo documento a partir dos dados importados
state.documents[tabId] = {
id: tabId,
name: docData.name || `Documento Importado ${tabId}`,
author: docData.author || "",
created: docData.created || new Date().toISOString(),
modified: docData.modified || new Date().toISOString(),
pages: {},
pageOrder: docData.pageOrder || [],
activePage: docData.activePage || 1
};
// Adicionar páginas
docData.pages.forEach(pageData => {
state.documents[tabId].pages[pageData.id] = {
id: pageData.id,
title: pageData.title || `Página ${pageData.id}`,
content: pageData.content || "",
backgroundColor: pageData.backgroundColor || "#ffffff",
order: pageData.order || 1
};
// Atualizar próximo ID de página se necessário
if (pageData.id >= state.nextPageId) {
state.nextPageId = pageData.id + 1;
}
});
// Atualizar interface
updateTabsUI();
switchTab(tabId);
closeModal('loadModal');
showNotification('Documento carregado com sucesso!');
} catch (error) {
alert('Erro ao importar documento: ' + error.message);
console.error(error);
}
}
// Exportar documento
function exportDocument() {
saveDocument();
}
// IMPRESSÃO
function printDocument() {
// Primeiro, salvar o conteúdo atual
savePageContent();
// Criar pré-visualização
createPrintPreview();
// Mostrar modal de pré-visualização
openModal('printPreviewModal');
}
function createPrintPreview() {
const doc = state.documents[state.currentTabId];
const page = doc.pages[state.currentPageId];
const previewContent = document.getElementById('printPreviewContent');
// Criar conteúdo de impressão
let printHTML = `
<div style="font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 800px; margin: 0 auto;">
<div style="border-bottom: 2px solid #0066cc; padding-bottom: 10px; margin-bottom: 20px;">
<h1 style="color: #0066cc; margin: 0;">${doc.name}</h1>
${doc.author ? `<p style="color: #666; margin: 5px 0 0 0;">Autor: ${doc.author}</p>` : ''}
<p style="color: #666; margin: 5px 0 0 0;">Página: ${page.title}</p>
<p style="color: #666; margin: 5px 0 0 0;">Data: ${new Date().toLocaleDateString('pt-BR')}</p>
</div>
<div id="printContent">
${page.content}
</div>
<div style="border-top: 1px solid #ddd; margin-top: 30px; padding-top: 10px; text-align: center; color: #666; font-size: 0.9rem;">
<p>Documento criado com Editor Avançado</p>
</div>
</div>
`;
previewContent.innerHTML = printHTML;
// Ajustar estilos para impressão
const printContent = previewContent.querySelector('#printContent');
if (printContent) {
// Aplicar estilos de impressão ao conteúdo
const elements = printContent.querySelectorAll('*');
elements.forEach(el => {
el.style.color = '#000000';
el.style.backgroundColor = 'transparent';
});
// Ajustar imagens para não quebrar
const images = printContent.querySelectorAll('img');
images.forEach(img => {
img.style.maxWidth = '100%';
img.style.height = 'auto';
});
}
}
function executePrint() {
// Fechar modal de pré-visualização
closeModal('printPreviewModal');
// Aguardar um momento para garantir que o modal foi fechado
setTimeout(() => {
// Usar a API de impressão do navegador
window.print();
}, 100);
}
// Gerenciamento de localStorage
function saveStateToStorage() {
try {
const stateToSave = {
documents: state.documents,
currentTabId: state.currentTabId,
currentPageId: state.currentPageId,
nextTabId: state.nextTabId,
nextPageId: state.nextPageId,
autoSave: state.autoSave,
spellCheck: state.spellCheck,
lastSaveTime: new Date().toISOString()
};
localStorage.setItem('advancedEditorState', JSON.stringify(stateToSave));
} catch (error) {
console.error('Erro ao salvar estado:', error);
}
}
function loadStateFromStorage() {
try {
const savedState = localStorage.getItem('advancedEditorState');
if (savedState) {
const parsedState = JSON.parse(savedState);
// Mesclar com estado atual (preservando documentos padrão se não houver salvos)
if (parsedState.documents && Object.keys(parsedState.documents).length > 0) {
state.documents = parsedState.documents;
state.currentTabId = parsedState.currentTabId || 1;
state.currentPageId = parsedState.currentPageId || 1;
state.nextTabId = parsedState.nextTabId || 2;
state.nextPageId = parsedState.nextPageId || 2;
state.autoSave = parsedState.autoSave || false;
state.spellCheck = parsedState.spellCheck !== undefined ? parsedState.spellCheck : true;
state.lastSaveTime = parsedState.lastSaveTime;
// Atualizar interface
updateTabsUI();
switchTab(state.currentTabId);
}
}
} catch (error) {
console.error('Erro ao carregar estado:', error);
}
}
function saveDocumentToLocalStorage(docData) {
try {
const savedDocs = JSON.parse(localStorage.getItem('savedDocuments') || '[]');
// Adicionar ou atualizar documento na lista
const existingIndex = savedDocs.findIndex(doc => doc.document.id === docData.document.id);
if (existingIndex >= 0) {
savedDocs[existingIndex] = docData;
} else {
savedDocs.push(docData);
}
// Manter apenas os 10 documentos mais recentes
if (savedDocs.length > 10) {
savedDocs.sort((a, b) => new Date(b.exportDate) - new Date(a.exportDate));
savedDocs.splice(10);
}
localStorage.setItem('savedDocuments', JSON.stringify(savedDocs));
} catch (error) {
console.error('Erro ao salvar documento na lista:', error);
}
}
function loadSavedDocumentsList() {
try {
const savedDocs = JSON.parse(localStorage.getItem('savedDocuments') || '[]');
const container = document.getElementById('savedDocuments');
container.innerHTML = '';
if (savedDocs.length === 0) {
container.innerHTML = '<p style="color: #666; font-style: italic;">Nenhum documento salvo localmente</p>';
return;
}
savedDocs.forEach((docData, index) => {
const doc = docData.document;
const docElement = document.createElement('div');
docElement.className = 'page-item';
docElement.style.marginBottom = '10px';
docElement.style.cursor = 'pointer';
docElement.innerHTML = `
<div style="display: flex; justify-content: space-between; align-items: center; width: 100%;">
<div>
<strong>${doc.name}</strong><br>
<small style="color: #666;">${doc.author || 'Sem autor'} • ${new Date(doc.modified).toLocaleDateString('pt-BR')}</small>
</div>
<button class="btn btn-secondary" onclick="loadSavedDocument(${index})" style="padding: 5px 10px; font-size: 0.8rem;">
Carregar
</button>
</div>
`;
container.appendChild(docElement);
});
} catch (error) {
console.error('Erro ao carregar lista de documentos:', error);
}
}
function loadSavedDocument(index) {
try {
const savedDocs = JSON.parse(localStorage.getItem('savedDocuments') || '[]');
if (savedDocs[index]) {
importDocumentData(savedDocs[index]);
}
} catch (error) {
console.error('Erro ao carregar documento salvo:', error);
}
}
// Função de notificação
function showNotification(message) {
// Criar elemento de notificação
const notification = document.createElement('div');
notification.textContent = message;
notification.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
background: #0066cc;
color: white;
padding: 12px 20px;
border-radius: 8px;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
z-index: 1000;
font-weight: bold;
animation: slideIn 0.3s ease-out;
`;
document.body.appendChild(notification);
// Remover após 3 segundos
setTimeout(() => {
notification.style.animation = 'slideOut 0.3s ease-in';
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}, 3000);
// Adicionar estilos de animação se não existirem
if (!document.querySelector('#notification-styles')) {
const style = document.createElement('style');
style.id = 'notification-styles';
style.textContent = `
@keyframes slideIn {
from { transform: translateY(100%); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes slideOut {
from { transform: translateY(0); opacity: 1; }
to { transform: translateY(100%); opacity: 0; }
}
`;
document.head.appendChild(style);
}
}
</script>
</body>
</html>
Comentários
Postar um comentário