{"id":2721,"date":"2025-07-22T15:21:35","date_gmt":"2025-07-22T20:21:35","guid":{"rendered":"https:\/\/biblioteca.utc.edu.ec\/?page_id=2721"},"modified":"2025-07-22T17:18:59","modified_gmt":"2025-07-22T22:18:59","slug":"prueba-3","status":"publish","type":"page","link":"https:\/\/biblioteca.utc.edu.ec\/?page_id=2721","title":{"rendered":"Tools-RIS"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"es\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Analizador de Archivos RIS &#8211; Herramienta de Investigaci\u00f3n<\/title>\n    <script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\n    <style>\n        * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n        }\n\n        body {\n            font-family: \"Segoe UI\", Tahoma, Geneva, Verdana, sans-serif;\n            background: linear-gradient(135deg, #ffffff 0%, #ffffff 100%);\n            min-height: 100vh;\n            color: #333;\n        }\n\n        .container {\n            max-width: 1400px;\n            margin: 0 auto;\n            padding: 20px;\n        }\n\n        .header {\n            text-align: center;\n            background: rgba(255, 255, 255, 0.95);\n            padding: 30px;\n            border-radius: 15px;\n            margin-bottom: 30px;\n            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);\n        }\n\n        .header h1 {\n            font-size: 1.5rem;\n            margin-bottom: 10px;\n            background: linear-gradient(45deg, #667eea, #764ba2);\n            -webkit-background-clip: text;\n            -webkit-text-fill-color: transparent;\n            background-clip: text;\n        }\n\n        .header p {\n            font-size: 1.1rem;\n            color: #666;\n        }\n\n        .import-section {\n            background: white;\n            border-radius: 15px;\n            padding: 30px;\n            margin-bottom: 30px;\n            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);\n        }\n\n        .upload-area {\n            border: 3px dashed #667eea;\n            border-radius: 15px;\n            padding: 50px;\n            text-align: center;\n            transition: all 0.3s ease;\n            cursor: pointer;\n        }\n\n        .upload-area:hover {\n            border-color: #764ba2;\n            background: rgba(102, 126, 234, 0.05);\n        }\n\n        .upload-area.dragover {\n            border-color: #764ba2;\n            background: rgba(102, 126, 234, 0.1);\n        }\n\n        .upload-icon {\n            font-size: 4rem;\n            margin-bottom: 20px;\n        }\n\n        .upload-content h3 {\n            font-size: 1rem;\n            margin-bottom: 10px;\n            color: #333;\n        }\n\n        .upload-content p {\n            color: #666;\n            margin-bottom: 20px;\n        }\n\n        .btn-primary {\n            background: linear-gradient(45deg, #667eea, #764ba2);\n            color: white;\n            border: none;\n            padding: 12px 30px;\n            border-radius: 25px;\n            font-size: 0.8rem;\n            cursor: pointer;\n            transition: all 0.3s ease;\n        }\n\n        .btn-primary:hover {\n            transform: translateY(-2px);\n            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);\n        }\n\n        .btn-secondary {\n            background: #f8f9fa;\n            color: #333;\n            border: 2px solid #dee2e6;\n            padding: 10px 20px;\n            border-radius: 20px;\n            cursor: pointer;\n            transition: all 0.3s ease;\n        }\n\n        .btn-secondary:hover {\n            background: #e9ecef;\n            border-color: #adb5bd;\n        }\n\n        .file-info {\n            background: #f8f9fa;\n            padding: 20px;\n            border-radius: 10px;\n            margin-top: 20px;\n        }\n\n        .stats-section {\n            background: white;\n            border-radius: 15px;\n            padding: 30px;\n            margin-bottom: 30px;\n            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);\n        }\n\n        .stats-section h2 {\n            margin-bottom: 25px;\n            color: #333;\n        }\n\n        .stats-grid {\n            display: grid;\n            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n            gap: 20px;\n        }\n\n        .stat-card {\n            background: linear-gradient(135deg, #667eea, #764ba2);\n            color: white;\n            padding: 25px;\n            border-radius: 15px;\n            text-align: center;\n            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);\n        }\n\n        .stat-number {\n            font-size: 1rem;\n            font-weight: bold;\n            margin-bottom: 10px;\n        }\n\n        .stat-label {\n            font-size: 0.7rem;\n            opacity: 0.9;\n        }\n\n        .analysis-section {\n            background: white;\n            border-radius: 15px;\n            padding: 30px;\n            margin-bottom: 30px;\n            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);\n        }\n\n        .tabs {\n            display: flex;\n            flex-wrap: wrap;\n            gap: 10px;\n            margin-bottom: 30px;\n            border-bottom: 2px solid #f1f3f4;\n            padding-bottom: 15px;\n        }\n\n        .tab-button {\n            background: #8abef1;\n            border: none;\n            padding: 12px 20px;\n            border-radius: 25px;\n            cursor: pointer;\n            transition: all 0.3s ease;\n            font-size: 0.9rem;\n        }\n\n        .tab-button.active {\n            background: linear-gradient(45deg, #667eea, #764ba2);\n            color: white;\n        }\n\n        .tab-button:hover:not(.active) {\n            background: linear-gradient(45deg, #667eea, #764ba2);\n        }\n\n        .tab-content {\n            display: none;\n        }\n\n        .tab-content.active {\n            display: block;\n        }\n\n        .analysis-grid {\n            display: grid;\n            grid-template-columns: 1fr 1fr;\n            gap: 30px;\n            align-items: start;\n        }\n\n        .chart-container {\n            background: #f8f9fa;\n            padding: 20px;\n            border-radius: 15px;\n            height: 400px;\n        }\n\n        .data-table h4 {\n            margin-bottom: 15px;\n            color: #333;\n        }\n\n        .table-container {\n            max-height: 400px;\n            overflow-y: auto;\n            border: 1px solid #dee2e6;\n            border-radius: 10px;\n        }\n\n        table {\n            width: 100%;\n            border-collapse: collapse;\n        }\n\n        th,\n        td {\n            padding: 12px;\n            text-align: left;\n            border-bottom: 1px solid #dee2e6;\n        }\n\n        th {\n            background: #f8f9fa;\n            font-weight: 600;\n            position: sticky;\n            top: 0;\n            z-index: 10;\n        }\n\n        tr:hover {\n            background: rgba(102, 126, 234, 0.05);\n        }\n\n        .search-section {\n            background: white;\n            border-radius: 15px;\n            padding: 30px;\n            margin-bottom: 30px;\n            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);\n        }\n\n        .search-controls {\n            display: flex;\n            gap: 15px;\n            margin-bottom: 25px;\n            flex-wrap: wrap;\n        }\n\n        .search-controls input,\n        .search-controls select {\n            padding: 10px;\n            border: 2px solid #dee2e6;\n            border-radius: 10px;\n            font-size: 0.8rem;\n            flex: 1;\n            min-width: 200px;\n        }\n\n        .search-controls input:focus,\n        .search-controls select:focus {\n            outline: none;\n            border-color: #667eea;\n        }\n\n        .search-results {\n            background: #f8f9fa;\n            padding: 20px;\n            border-radius: 10px;\n            max-height: 400px;\n            overflow-y: auto;\n        }\n\n        .search-result-item {\n            background: white;\n            padding: 15px;\n            margin-bottom: 10px;\n            border-radius: 8px;\n            border-left: 4px solid #667eea;\n        }\n\n        .search-result-item h4 {\n            margin-bottom: 5px;\n            color: #333;\n        }\n\n        .search-result-item p {\n            color: #666;\n            font-size: 0.5rem;\n            margin-bottom: 3px;\n        }\n\n        .export-section {\n            background: white;\n            border-radius: 15px;\n            padding: 30px;\n            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);\n        }\n\n        .export-controls {\n            display: flex;\n            gap: 15px;\n            flex-wrap: wrap;\n        }\n\n        .loading {\n            display: inline-block;\n            width: 20px;\n            height: 20px;\n            border: 3px solid #f3f3f3;\n            border-top: 3px solid #667eea;\n            border-radius: 50%;\n            animation: spin 1s linear infinite;\n        }\n\n        @keyframes spin {\n            0% {\n                transform: rotate(0deg);\n            }\n            100% {\n                transform: rotate(360deg);\n            }\n        }\n\n        @media (max-width: 768px) {\n            .analysis-grid {\n                grid-template-columns: 1fr;\n            }\n\n            .search-controls {\n                flex-direction: column;\n            }\n\n            .export-controls {\n                flex-direction: column;\n            }\n\n            .tabs {\n                justify-content: center;\n            }\n\n            .tab-button {\n                font-size: 0.8rem;\n                padding: 10px 15px;\n            }\n        }\n\n        .debug-info {\n            background: #f8f9fa;\n            padding: 15px;\n            border-radius: 10px;\n            margin-top: 15px;\n            font-family: monospace;\n            font-size: 0.7rem;\n            max-height: 200px;\n            overflow-y: auto;\n        }\n\n        \/* Estilos para el input file visible *\/\n        .file-input-container {\n            margin-top: 20px;\n        }\n\n        .file-input-visible {\n            display: block;\n            width: 100%;\n            padding: 12px;\n            border: 2px solid #667eea;\n            border-radius: 10px;\n            background: white;\n            cursor: pointer;\n            font-size: 0.7rem;\n        }\n\n        .file-input-visible:hover {\n            border-color: #764ba2;\n            background: rgba(102, 126, 234, 0.05);\n        }\n    <\/style>\n<\/head>\n<body>\n    <div class=\"container\">\n        <header class=\"header\">\n            <h1>\ud83d\udcca Analizador de Archivos RIS<\/h1>\n            <p>Herramienta avanzada para an\u00e1lisis bibliom\u00e9trico y estad\u00edstico<\/p>\n        <\/header>\n\n        <main class=\"main-content\">\n            <!-- Secci\u00f3n de importaci\u00f3n -->\n            <section class=\"import-section\">\n                <div class=\"upload-area\" id=\"uploadArea\">\n                    <div class=\"upload-content\">\n                        <div class=\"upload-icon\">\ud83d\udcc1<\/div>\n                        <h3>Importar Archivo RIS<\/h3>\n                        <p>Arrastra tu archivo RIS aqu\u00ed o selecciona un archivo abajo<\/p>\n                        \n                        <!-- Input file visible y funcional -->\n                        <div class=\"file-input-container\">\n                            <input type=\"file\" id=\"fileInput\" accept=\".ris,.txt\" class=\"file-input-visible\">\n                        <\/div>\n                    <\/div>\n                <\/div>\n                <div class=\"file-info\" id=\"fileInfo\" style=\"display: none;\">\n                    <h4>Archivo cargado:<\/h4>\n                    <p id=\"fileName\"><\/p>\n                    <p id=\"fileStats\"><\/p>\n                    <div class=\"debug-info\" id=\"debugInfo\"><\/div>\n                <\/div>\n            <\/section>\n\n            <!-- Secci\u00f3n de estad\u00edsticas generales -->\n            <section class=\"stats-section\" id=\"statsSection\" style=\"display: none;\">\n                <h4>\ud83d\udcc8 Estad\u00edsticas Generales<\/h4>\n                <div class=\"stats-grid\">\n                    <div class=\"stat-card\">\n                        <div class=\"stat-number\" id=\"totalRecords\">0<\/div>\n                        <div class=\"stat-label\">Total de Registros<\/div>\n                    <\/div>\n                    <div class=\"stat-card\">\n                        <div class=\"stat-number\" id=\"uniqueAuthors\">0<\/div>\n                        <div class=\"stat-label\">Autores \u00danicos<\/div>\n                    <\/div>\n                    <div class=\"stat-card\">\n                        <div class=\"stat-number\" id=\"yearRange\">&#8211;<\/div>\n                        <div class=\"stat-label\">Rango de A\u00f1os<\/div>\n                    <\/div>\n                    <div class=\"stat-card\">\n                        <div class=\"stat-number\" id=\"uniqueJournals\">0<\/div>\n                        <div class=\"stat-label\">Revistas \u00danicas<\/div>\n                    <\/div>\n                <\/div>\n            <\/section>\n\n            <!-- Secci\u00f3n de an\u00e1lisis -->\n            <section class=\"analysis-section\" id=\"analysisSection\" style=\"display: none;\">\n                <div class=\"tabs\">\n                    <button class=\"tab-button active\" onclick=\"showTab('authors')\">\ud83d\udc65 Autores<\/button>\n                    <button class=\"tab-button\" onclick=\"showTab('years')\">\ud83d\udcc5 A\u00f1os<\/button>\n                    <button class=\"tab-button\" onclick=\"showTab('journals')\">\ud83d\udcda Revistas<\/button>\n                    <button class=\"tab-button\" onclick=\"showTab('types')\">\ud83d\udcc4 Tipos<\/button>\n                    <button class=\"tab-button\" onclick=\"showTab('keywords')\">\ud83c\udff7\ufe0f Palabras Clave<\/button>\n                    <button class=\"tab-button\" onclick=\"showTab('fields')\">\ud83d\udd0d Campos RIS<\/button>\n                <\/div>\n\n                <!-- Tab de Autores -->\n                <div class=\"tab-content active\" id=\"authors-tab\">\n                    <h4>An\u00e1lisis de Autores<\/h4>\n                    <div class=\"analysis-grid\">\n                        <div class=\"chart-container\">\n                            <canvas id=\"authorsChart\"><\/canvas>\n                        <\/div>\n                        <div class=\"data-table\">\n                            <h4>Top 20 Autores m\u00e1s Prol\u00edficos<\/h4>\n                            <div class=\"table-container\">\n                                <table id=\"authorsTable\">\n                                    <thead>\n                                        <tr>\n                                            <th>Autor<\/th>\n                                            <th>Publicaciones<\/th>\n                                            <th>%<\/th>\n                                        <\/tr>\n                                    <\/thead>\n                                    <tbody><\/tbody>\n                                <\/table>\n                            <\/div>\n                        <\/div>\n                    <\/div>\n                <\/div>\n\n                <!-- Tab de A\u00f1os -->\n                <div class=\"tab-content\" id=\"years-tab\">\n                    <h3>Distribuci\u00f3n Temporal<\/h3>\n                    <div class=\"analysis-grid\">\n                        <div class=\"chart-container\">\n                            <canvas id=\"yearsChart\"><\/canvas>\n                        <\/div>\n                        <div class=\"data-table\">\n                            <h4>Publicaciones por A\u00f1o<\/h4>\n                            <div class=\"table-container\">\n                                <table id=\"yearsTable\">\n                                    <thead>\n                                        <tr>\n                                            <th>A\u00f1o<\/th>\n                                            <th>Publicaciones<\/th>\n                                            <th>%<\/th>\n                                        <\/tr>\n                                    <\/thead>\n                                    <tbody><\/tbody>\n                                <\/table>\n                            <\/div>\n                        <\/div>\n                    <\/div>\n                <\/div>\n\n                <!-- Tab de Revistas -->\n                <div class=\"tab-content\" id=\"journals-tab\">\n                    <h4>An\u00e1lisis de Revistas<\/h4>\n                    <div class=\"analysis-grid\">\n                        <div class=\"chart-container\">\n                            <canvas id=\"journalsChart\"><\/canvas>\n                        <\/div>\n                        <div class=\"data-table\">\n                            <h4>Revistas m\u00e1s Utilizadas<\/h4>\n                            <div class=\"table-container\">\n                                <table id=\"journalsTable\">\n                                    <thead>\n                                        <tr>\n                                            <th>Revista<\/th>\n                                            <th>Art\u00edculos<\/th>\n                                            <th>%<\/th>\n                                        <\/tr>\n                                    <\/thead>\n                                    <tbody><\/tbody>\n                                <\/table>\n                            <\/div>\n                        <\/div>\n                    <\/div>\n                <\/div>\n\n                <!-- Tab de Tipos -->\n                <div class=\"tab-content\" id=\"types-tab\">\n                    <h4>Tipos de Documentos<\/h4>\n                    <div class=\"analysis-grid\">\n                        <div class=\"chart-container\">\n                            <canvas id=\"typesChart\"><\/canvas>\n                        <\/div>\n                        <div class=\"data-table\">\n                            <h4>Distribuci\u00f3n por Tipo<\/h4>\n                            <div class=\"table-container\">\n                                <table id=\"typesTable\">\n                                    <thead>\n                                        <tr>\n                                            <th>Tipo<\/th>\n                                            <th>Cantidad<\/th>\n                                            <th>%<\/th>\n                                        <\/tr>\n                                    <\/thead>\n                                    <tbody><\/tbody>\n                                <\/table>\n                            <\/div>\n                        <\/div>\n                    <\/div>\n                <\/div>\n\n                <!-- Tab de Palabras Clave -->\n                <div class=\"tab-content\" id=\"keywords-tab\">\n                    <h4>An\u00e1lisis de Palabras Clave<\/h4>\n                    <div class=\"analysis-grid\">\n                        <div class=\"chart-container\">\n                            <canvas id=\"keywordsChart\"><\/canvas>\n                        <\/div>\n                        <div class=\"data-table\">\n                            <h4>Palabras Clave m\u00e1s Frecuentes<\/h4>\n                            <div class=\"table-container\">\n                                <table id=\"keywordsTable\">\n                                    <thead>\n                                        <tr>\n                                            <th>Palabra Clave<\/th>\n                                            <th>Frecuencia<\/th>\n                                            <th>%<\/th>\n                                        <\/tr>\n                                    <\/thead>\n                                    <tbody><\/tbody>\n                                <\/table>\n                            <\/div>\n                        <\/div>\n                    <\/div>\n                <\/div>\n\n                <!-- Tab de Campos RIS -->\n                <div class=\"tab-content\" id=\"fields-tab\">\n                    <h4>An\u00e1lisis de Campos RIS<\/h4>\n                    <div class=\"analysis-grid\">\n                        <div class=\"chart-container\">\n                            <canvas id=\"fieldsChart\"><\/canvas>\n                        <\/div>\n                        <div class=\"data-table\">\n                            <h4>Frecuencia de Campos<\/h4>\n                            <div class=\"table-container\">\n                                <table id=\"fieldsTable\">\n                                    <thead>\n                                        <tr>\n                                            <th>Campo<\/th>\n                                            <th>Descripci\u00f3n<\/th>\n                                            <th>Frecuencia<\/th>\n                                            <th>%<\/th>\n                                        <\/tr>\n                                    <\/thead>\n                                    <tbody><\/tbody>\n                                <\/table>\n                            <\/div>\n                        <\/div>\n                    <\/div>\n                <\/div>\n            <\/section>\n\n            <!-- Secci\u00f3n de b\u00fasqueda y filtros -->\n            <section class=\"search-section\" id=\"searchSection\" style=\"display: none;\">\n                <h4>\ud83d\udd0d B\u00fasqueda y Filtros<\/h4>\n                <div class=\"search-controls\">\n                    <input type=\"text\" id=\"searchInput\" placeholder=\"Buscar en t\u00edtulos, autores, palabras clave...\">\n                    <select id=\"yearFilter\">\n                        <option value=\"\">Todos los a\u00f1os<\/option>\n                    <\/select>\n                    <select id=\"typeFilter\">\n                        <option value=\"\">Todos los tipos<\/option>\n                    <\/select>\n                    <button class=\"btn-secondary\" onclick=\"clearFilters()\">Limpiar Filtros<\/button>\n                <\/div>\n                <div class=\"search-results\" id=\"searchResults\">\n                    <p>Utiliza los filtros arriba para buscar en tu bibliograf\u00eda<\/p>\n                <\/div>\n            <\/section>\n\n            <!-- Secci\u00f3n de exportaci\u00f3n -->\n            <section class=\"export-section\" id=\"exportSection\" style=\"display: none;\">\n                <h4>\ud83d\udce4 Exportar Resultados<\/h4>\n                <div class=\"export-controls\">\n                    <button class=\"btn-primary\" onclick=\"exportToCSV()\">Exportar a CSV<\/button>\n                    <button class=\"btn-primary\" onclick=\"exportToJSON()\">Exportar a JSON<\/button>\n                    <button class=\"btn-primary\" onclick=\"generateReport()\">Generar Reporte<\/button>\n                <\/div>\n            <\/section>\n        <\/main>\n    <\/div>\n\n    <script>\n        class RISAnalyzer {\n            constructor() {\n                this.data = []\n                this.parsedData = {\n                    records: [],\n                    authors: {},\n                    years: {},\n                    journals: {},\n                    types: {},\n                    keywords: {},\n                    fields: {},\n                }\n                this.charts = {}\n                this.initializeEventListeners()\n            }\n\n            initializeEventListeners() {\n                const fileInput = document.getElementById(\"fileInput\")\n                const uploadArea = document.getElementById(\"uploadArea\")\n                const searchInput = document.getElementById(\"searchInput\")\n                const yearFilter = document.getElementById(\"yearFilter\")\n                const typeFilter = document.getElementById(\"typeFilter\")\n\n                \/\/ Event listener para el input file\n                fileInput.addEventListener(\"change\", (e) => {\n                    console.log(\"Archivo seleccionado:\", e.target.files[0])\n                    this.handleFileSelect(e)\n                })\n\n                \/\/ Event listeners para drag and drop\n                uploadArea.addEventListener(\"dragover\", (e) => this.handleDragOver(e))\n                uploadArea.addEventListener(\"drop\", (e) => this.handleDrop(e))\n                uploadArea.addEventListener(\"dragleave\", (e) => this.handleDragLeave(e))\n\n                \/\/ Event listeners para b\u00fasqueda\n                if (searchInput) searchInput.addEventListener(\"input\", () => this.performSearch())\n                if (yearFilter) yearFilter.addEventListener(\"change\", () => this.performSearch())\n                if (typeFilter) typeFilter.addEventListener(\"change\", () => this.performSearch())\n            }\n\n            handleDragOver(e) {\n                e.preventDefault()\n                document.getElementById(\"uploadArea\").classList.add(\"dragover\")\n            }\n\n            handleDragLeave(e) {\n                e.preventDefault()\n                document.getElementById(\"uploadArea\").classList.remove(\"dragover\")\n            }\n\n            handleDrop(e) {\n                e.preventDefault()\n                document.getElementById(\"uploadArea\").classList.remove(\"dragover\")\n                const files = e.dataTransfer.files\n                if (files.length > 0) {\n                    console.log(\"Archivo arrastrado:\", files[0])\n                    this.processFile(files[0])\n                }\n            }\n\n            handleFileSelect(e) {\n                const file = e.target.files[0]\n                if (file) {\n                    console.log(\"Procesando archivo:\", file.name, file.type, file.size)\n                    this.processFile(file)\n                } else {\n                    console.log(\"No se seleccion\u00f3 ning\u00fan archivo\")\n                }\n            }\n\n            async processFile(file) {\n                try {\n                    console.log(\"Iniciando procesamiento del archivo...\")\n                    const text = await this.readFile(file)\n                    console.log(\"Archivo le\u00eddo, longitud:\", text.length)\n                    \n                    this.showFileInfo(file, text)\n                    this.parseRISData(text)\n                    this.generateStatistics()\n                    this.showAnalysis()\n                    \n                    console.log(\"Procesamiento completado exitosamente\")\n                } catch (error) {\n                    console.error(\"Error completo:\", error)\n                    alert(\"Error al procesar el archivo: \" + error.message)\n                }\n            }\n\n            readFile(file) {\n                return new Promise((resolve, reject) => {\n                    const reader = new FileReader()\n                    reader.onload = (e) => {\n                        console.log(\"FileReader onload ejecutado\")\n                        resolve(e.target.result)\n                    }\n                    reader.onerror = (e) => {\n                        console.error(\"FileReader error:\", e)\n                        reject(new Error(\"Error al leer el archivo\"))\n                    }\n                    reader.readAsText(file, \"UTF-8\")\n                })\n            }\n\n            showFileInfo(file, content) {\n                const fileInfo = document.getElementById(\"fileInfo\")\n                const fileName = document.getElementById(\"fileName\")\n                const fileStats = document.getElementById(\"fileStats\")\n                const debugInfo = document.getElementById(\"debugInfo\")\n\n                fileName.textContent = file.name\n                const lines = content.split(\"\\n\").length\n                const size = (file.size \/ 1024).toFixed(2)\n                fileStats.textContent = `Tama\u00f1o: ${size} KB | L\u00edneas: ${lines}`\n\n                \/\/ Debug info\n                const firstLines = content.split(\"\\n\").slice(0, 10).join(\"\\n\")\n                debugInfo.textContent = `Primeras 10 l\u00edneas:\\n${firstLines}`\n\n                fileInfo.style.display = \"block\"\n            }\n\n            parseRISData(text) {\n                console.log(\"Iniciando parsing del archivo RIS...\")\n                const lines = text.split(\/\\r?\\n\/)\n                let currentRecord = {}\n                const records = []\n                let lineNumber = 0\n\n                for (let line of lines) {\n                    lineNumber++\n                    line = line.trim()\n                    \n                    if (line === \"\") continue\n\n                    \/\/ Detectar inicio de registro\n                    if (line.startsWith(\"TY  - \")) {\n                        \/\/ Si ya hay un registro en proceso, guardarlo\n                        if (Object.keys(currentRecord).length > 0) {\n                            records.push(currentRecord)\n                        }\n                        currentRecord = {}\n                        currentRecord.TY = line.substring(6).trim()\n                        console.log(`Nuevo registro encontrado en l\u00ednea ${lineNumber}: ${currentRecord.TY}`)\n                    } \n                    \/\/ Detectar fin de registro\n                    else if (line.startsWith(\"ER  -\")) {\n                        if (Object.keys(currentRecord).length > 0) {\n                            records.push(currentRecord)\n                            console.log(`Registro completado. Total de campos: ${Object.keys(currentRecord).length}`)\n                            currentRecord = {}\n                        }\n                    } \n                    \/\/ Procesar campos de datos\n                    else if (line.match(\/^[A-Z0-9]{2}\\s\\s-\\s\/)) {\n                        const tag = line.substring(0, 2)\n                        const value = line.substring(6).trim()\n\n                        if (currentRecord[tag]) {\n                            \/\/ Si el campo ya existe, convertir a array o agregar al array existente\n                            if (Array.isArray(currentRecord[tag])) {\n                                currentRecord[tag].push(value)\n                            } else {\n                                currentRecord[tag] = [currentRecord[tag], value]\n                            }\n                        } else {\n                            currentRecord[tag] = value\n                        }\n                    }\n                    \/\/ Manejar l\u00edneas de continuaci\u00f3n (campos que contin\u00faan en la siguiente l\u00ednea)\n                    else if (line.length > 0 && Object.keys(currentRecord).length > 0) {\n                        \/\/ Esta es una l\u00ednea de continuaci\u00f3n del \u00faltimo campo\n                        const lastField = Object.keys(currentRecord).pop()\n                        if (lastField) {\n                            if (Array.isArray(currentRecord[lastField])) {\n                                const lastIndex = currentRecord[lastField].length - 1\n                                currentRecord[lastField][lastIndex] += \" \" + line\n                            } else {\n                                currentRecord[lastField] += \" \" + line\n                            }\n                        }\n                    }\n                }\n\n                \/\/ Agregar el \u00faltimo registro si existe\n                if (Object.keys(currentRecord).length > 0) {\n                    records.push(currentRecord)\n                }\n\n                console.log(`Parsing completado. Total de registros: ${records.length}`)\n                if (records.length > 0) {\n                    console.log(\"Primer registro:\", records[0])\n                }\n\n                this.parsedData.records = records\n                this.analyzeFields()\n            }\n\n            analyzeFields() {\n                const { records } = this.parsedData\n                console.log(`Analizando ${records.length} registros...`)\n\n                \/\/ Reset counters\n                this.parsedData.authors = {}\n                this.parsedData.years = {}\n                this.parsedData.journals = {}\n                this.parsedData.types = {}\n                this.parsedData.keywords = {}\n                this.parsedData.fields = {}\n\n                records.forEach((record, index) => {\n                    console.log(`Analizando registro ${index + 1}:`, Object.keys(record))\n\n                    \/\/ Analyze fields\n                    Object.keys(record).forEach((field) => {\n                        this.parsedData.fields[field] = (this.parsedData.fields[field] || 0) + 1\n                    })\n\n                    \/\/ Analyze authors - campos AU principalmente\n                    if (record.AU) {\n                        const authors = Array.isArray(record.AU) ? record.AU : [record.AU]\n                        authors.forEach((author) => {\n                            const cleanAuthor = this.cleanAuthorName(author)\n                            this.parsedData.authors[cleanAuthor] = (this.parsedData.authors[cleanAuthor] || 0) + 1\n                        })\n                    }\n\n                    \/\/ Analyze years - campo PY principalmente\n                    if (record.PY) {\n                        const year = record.PY.toString().substring(0, 4)\n                        if (year && year.match(\/^\\d{4}$\/)) {\n                            this.parsedData.years[year] = (this.parsedData.years[year] || 0) + 1\n                        }\n                    }\n\n                    \/\/ Analyze journals - campo T2 principalmente\n                    if (record.T2) {\n                        const journal = record.T2\n                        this.parsedData.journals[journal] = (this.parsedData.journals[journal] || 0) + 1\n                    }\n\n                    \/\/ Analyze types\n                    if (record.TY) {\n                        const type = this.getDocumentTypeDescription(record.TY)\n                        this.parsedData.types[type] = (this.parsedData.types[type] || 0) + 1\n                    }\n\n                    \/\/ Analyze keywords from title and abstract\n                    if (record.TI) {\n                        this.extractKeywordsFromText(record.TI).forEach((kw) => {\n                            this.parsedData.keywords[kw] = (this.parsedData.keywords[kw] || 0) + 1\n                        })\n                    }\n\n                    if (record.AB) {\n                        this.extractKeywordsFromText(record.AB).forEach((kw) => {\n                            this.parsedData.keywords[kw] = (this.parsedData.keywords[kw] || 0) + 1\n                        })\n                    }\n                })\n\n                console.log(\"An\u00e1lisis completado:\")\n                console.log(\"Autores \u00fanicos:\", Object.keys(this.parsedData.authors).length)\n                console.log(\"A\u00f1os \u00fanicos:\", Object.keys(this.parsedData.years).length)\n                console.log(\"Revistas \u00fanicas:\", Object.keys(this.parsedData.journals).length)\n                console.log(\"Tipos \u00fanicos:\", Object.keys(this.parsedData.types).length)\n                console.log(\"Keywords \u00fanicas:\", Object.keys(this.parsedData.keywords).length)\n            }\n\n            cleanAuthorName(author) {\n                return author.replace(\/,\\s*$\/, \"\").trim()\n            }\n\n            getDocumentTypeDescription(type) {\n                const types = {\n                    JOUR: \"Art\u00edculo de Revista\",\n                    BOOK: \"Libro\",\n                    CHAP: \"Cap\u00edtulo de Libro\",\n                    CONF: \"Art\u00edculo de Conferencia\",\n                    THES: \"Tesis\",\n                    RPRT: \"Reporte\",\n                    UNPB: \"No Publicado\",\n                    MGZN: \"Art\u00edculo de Revista\",\n                    NEWS: \"Art\u00edculo de Peri\u00f3dico\",\n                    WEB: \"P\u00e1gina Web\",\n                }\n                return types[type] || type\n            }\n\n            extractKeywordsFromText(text) {\n                if (!text) return []\n                \n                \/\/ Simple keyword extraction from text\n                const words = text\n                    .toLowerCase()\n                    .replace(\/[^\\w\\s]\/g, \" \")\n                    .split(\/\\s+\/)\n                    .filter((word) => word.length > 4)\n                    .filter((word) => !this.isStopWord(word))\n\n                const wordCount = {}\n                words.forEach((word) => {\n                    wordCount[word] = (wordCount[word] || 0) + 1\n                })\n\n                return Object.keys(wordCount)\n                    .filter((word) => wordCount[word] >= 1)\n                    .slice(0, 5)\n            }\n\n            isStopWord(word) {\n                const stopWords = [\n                    \"the\", \"and\", \"for\", \"are\", \"but\", \"not\", \"you\", \"all\", \"can\", \"had\", \"her\", \"was\", \"one\", \"our\", \"out\", \"day\", \"get\", \"has\", \"him\", \"his\", \"how\", \"its\", \"may\", \"new\", \"now\", \"old\", \"see\", \"two\", \"who\", \"boy\", \"did\", \"she\", \"use\", \"her\", \"way\", \"many\", \"then\", \"them\", \"well\", \"were\", \"been\", \"have\", \"there\", \"where\", \"much\", \"your\", \"work\", \"life\", \"only\", \"live\", \"year\", \"back\", \"with\", \"from\", \"they\", \"know\", \"want\", \"been\", \"good\", \"much\", \"some\", \"time\", \"very\", \"when\", \"come\", \"here\", \"just\", \"like\", \"long\", \"make\", \"over\", \"such\", \"take\", \"than\", \"them\", \"well\", \"were\", \"will\", \"would\", \"there\", \"each\", \"which\", \"their\", \"said\", \"will\", \"about\", \"after\", \"again\", \"before\", \"other\", \"many\", \"right\", \"still\", \"such\", \"through\", \"should\", \"because\", \"does\", \"most\", \"people\", \"over\", \"know\", \"water\", \"than\", \"call\", \"first\", \"who\", \"oil\", \"sit\", \"now\", \"find\", \"long\", \"down\", \"day\", \"did\", \"get\", \"come\", \"made\", \"may\", \"part\", \"this\", \"that\", \"with\", \"have\", \"from\", \"they\", \"been\", \"more\", \"what\", \"were\", \"said\", \"each\", \"which\", \"their\", \"time\", \"will\", \"about\", \"would\", \"there\", \"could\", \"other\", \"after\", \"first\", \"never\", \"these\", \"think\", \"where\", \"being\", \"every\", \"great\", \"might\", \"shall\", \"still\", \"those\", \"while\", \"years\", \"study\", \"using\", \"based\", \"data\", \"analysis\", \"method\", \"results\", \"research\", \"approach\", \"model\", \"system\", \"used\", \"also\", \"show\", \"found\", \"different\", \"between\", \"both\", \"however\", \"therefore\", \"thus\", \"moreover\", \"furthermore\", \"additionally\", \"finally\", \"conclusion\"\n                ]\n                return stopWords.includes(word.toLowerCase())\n            }\n\n            generateStatistics() {\n                const totalRecords = this.parsedData.records.length\n                const uniqueAuthors = Object.keys(this.parsedData.authors).length\n                const years = Object.keys(this.parsedData.years)\n                    .map((y) => parseInt(y))\n                    .filter((y) => !isNaN(y))\n                const yearRange = years.length > 0 ? `${Math.min(...years)} - ${Math.max(...years)}` : \"-\"\n                const uniqueJournals = Object.keys(this.parsedData.journals).length\n\n                document.getElementById(\"totalRecords\").textContent = totalRecords.toLocaleString()\n                document.getElementById(\"uniqueAuthors\").textContent = uniqueAuthors.toLocaleString()\n                document.getElementById(\"yearRange\").textContent = yearRange\n                document.getElementById(\"uniqueJournals\").textContent = uniqueJournals.toLocaleString()\n\n                this.populateFilters()\n            }\n\n            populateFilters() {\n                const yearFilter = document.getElementById(\"yearFilter\")\n                const typeFilter = document.getElementById(\"typeFilter\")\n\n                \/\/ Clear existing options\n                yearFilter.innerHTML = '<option value=\"\">Todos los a\u00f1os<\/option>'\n                typeFilter.innerHTML = '<option value=\"\">Todos los tipos<\/option>'\n\n                \/\/ Populate year filter\n                const years = Object.keys(this.parsedData.years).sort((a, b) => b - a)\n                years.forEach((year) => {\n                    const option = document.createElement(\"option\")\n                    option.value = year\n                    option.textContent = year\n                    yearFilter.appendChild(option)\n                })\n\n                \/\/ Populate type filter\n                const types = Object.keys(this.parsedData.types).sort()\n                types.forEach((type) => {\n                    const option = document.createElement(\"option\")\n                    option.value = type\n                    option.textContent = type\n                    typeFilter.appendChild(option)\n                })\n            }\n\n            showAnalysis() {\n                document.getElementById(\"statsSection\").style.display = \"block\"\n                document.getElementById(\"analysisSection\").style.display = \"block\"\n                document.getElementById(\"searchSection\").style.display = \"block\"\n                document.getElementById(\"exportSection\").style.display = \"block\"\n\n                this.generateCharts()\n                this.generateTables()\n            }\n\n            generateCharts() {\n                this.generateAuthorsChart()\n                this.generateYearsChart()\n                this.generateJournalsChart()\n                this.generateTypesChart()\n                this.generateKeywordsChart()\n                this.generateFieldsChart()\n            }\n\n            generateAuthorsChart() {\n                const ctx = document.getElementById(\"authorsChart\").getContext(\"2d\")\n                const sortedAuthors = Object.entries(this.parsedData.authors)\n                    .sort((a, b) => b[1] - a[1])\n                    .slice(0, 15)\n\n                if (this.charts.authors) {\n                    this.charts.authors.destroy()\n                }\n\n                this.charts.authors = new Chart(ctx, {\n                    type: \"bar\",\n                    data: {\n                        labels: sortedAuthors.map(([author]) => (author.length > 30 ? author.substring(0, 30) + \"...\" : author)),\n                        datasets: [\n                            {\n                                label: \"Publicaciones\",\n                                data: sortedAuthors.map(([, count]) => count),\n                                backgroundColor: \"rgba(102, 126, 234, 0.8)\",\n                                borderColor: \"rgba(102, 126, 234, 1)\",\n                                borderWidth: 1,\n                            },\n                        ],\n                    },\n                    options: {\n                        responsive: true,\n                        maintainAspectRatio: false,\n                        plugins: {\n                            title: {\n                                display: true,\n                                text: \"Top 15 Autores m\u00e1s Prol\u00edficos\",\n                            },\n                        },\n                        scales: {\n                            y: {\n                                beginAtZero: true,\n                            },\n                        },\n                    },\n                })\n            }\n\n            generateYearsChart() {\n                const ctx = document.getElementById(\"yearsChart\").getContext(\"2d\")\n                const sortedYears = Object.entries(this.parsedData.years).sort((a, b) => a[0] - b[0])\n\n                if (this.charts.years) {\n                    this.charts.years.destroy()\n                }\n\n                this.charts.years = new Chart(ctx, {\n                    type: \"line\",\n                    data: {\n                        labels: sortedYears.map(([year]) => year),\n                        datasets: [\n                            {\n                                label: \"Publicaciones\",\n                                data: sortedYears.map(([, count]) => count),\n                                backgroundColor: \"rgba(118, 75, 162, 0.2)\",\n                                borderColor: \"rgba(118, 75, 162, 1)\",\n                                borderWidth: 2,\n                                fill: true,\n                            },\n                        ],\n                    },\n                    options: {\n                        responsive: true,\n                        maintainAspectRatio: false,\n                        plugins: {\n                            title: {\n                                display: true,\n                                text: \"Distribuci\u00f3n Temporal de Publicaciones\",\n                            },\n                        },\n                        scales: {\n                            y: {\n                                beginAtZero: true,\n                            },\n                        },\n                    },\n                })\n            }\n\n            generateJournalsChart() {\n                const ctx = document.getElementById(\"journalsChart\").getContext(\"2d\")\n                const sortedJournals = Object.entries(this.parsedData.journals)\n                    .sort((a, b) => b[1] - a[1])\n                    .slice(0, 10)\n\n                if (this.charts.journals) {\n                    this.charts.journals.destroy()\n                }\n\n                this.charts.journals = new Chart(ctx, {\n                    type: \"doughnut\",\n                    data: {\n                        labels: sortedJournals.map(([journal]) => (journal.length > 40 ? journal.substring(0, 40) + \"...\" : journal)),\n                        datasets: [\n                            {\n                                data: sortedJournals.map(([, count]) => count),\n                                backgroundColor: [\n                                    \"rgba(102, 126, 234, 0.8)\",\n                                    \"rgba(118, 75, 162, 0.8)\",\n                                    \"rgba(255, 99, 132, 0.8)\",\n                                    \"rgba(54, 162, 235, 0.8)\",\n                                    \"rgba(255, 205, 86, 0.8)\",\n                                    \"rgba(75, 192, 192, 0.8)\",\n                                    \"rgba(153, 102, 255, 0.8)\",\n                                    \"rgba(255, 159, 64, 0.8)\",\n                                    \"rgba(199, 199, 199, 0.8)\",\n                                    \"rgba(83, 102, 255, 0.8)\",\n                                ],\n                            },\n                        ],\n                    },\n                    options: {\n                        responsive: true,\n                        maintainAspectRatio: false,\n                        plugins: {\n                            title: {\n                                display: true,\n                                text: \"Top 10 Revistas m\u00e1s Utilizadas\",\n                            },\n                            legend: {\n                                position: \"bottom\",\n                            },\n                        },\n                    },\n                })\n            }\n\n            generateTypesChart() {\n                const ctx = document.getElementById(\"typesChart\").getContext(\"2d\")\n                const sortedTypes = Object.entries(this.parsedData.types).sort((a, b) => b[1] - a[1])\n\n                if (this.charts.types) {\n                    this.charts.types.destroy()\n                }\n\n                this.charts.types = new Chart(ctx, {\n                    type: \"pie\",\n                    data: {\n                        labels: sortedTypes.map(([type]) => type),\n                        datasets: [\n                            {\n                                data: sortedTypes.map(([, count]) => count),\n                                backgroundColor: [\n                                    \"rgba(102, 126, 234, 0.8)\",\n                                    \"rgba(118, 75, 162, 0.8)\",\n                                    \"rgba(255, 99, 132, 0.8)\",\n                                    \"rgba(54, 162, 235, 0.8)\",\n                                    \"rgba(255, 205, 86, 0.8)\",\n                                    \"rgba(75, 192, 192, 0.8)\",\n                                ],\n                            },\n                        ],\n                    },\n                    options: {\n                        responsive: true,\n                        maintainAspectRatio: false,\n                        plugins: {\n                            title: {\n                                display: true,\n                                text: \"Distribuci\u00f3n por Tipo de Documento\",\n                            },\n                            legend: {\n                                position: \"bottom\",\n                            },\n                        },\n                    },\n                })\n            }\n\n            generateKeywordsChart() {\n                const ctx = document.getElementById(\"keywordsChart\").getContext(\"2d\")\n                const sortedKeywords = Object.entries(this.parsedData.keywords)\n                    .sort((a, b) => b[1] - a[1])\n                    .slice(0, 20)\n\n                if (this.charts.keywords) {\n                    this.charts.keywords.destroy()\n                }\n\n                this.charts.keywords = new Chart(ctx, {\n                    type: \"bar\",\n                    data: {\n                        labels: sortedKeywords.map(([keyword]) => (keyword.length > 20 ? keyword.substring(0, 20) + \"...\" : keyword)),\n                        datasets: [\n                            {\n                                label: \"Frecuencia\",\n                                data: sortedKeywords.map(([, count]) => count),\n                                backgroundColor: \"rgba(75, 192, 192, 0.8)\",\n                                borderColor: \"rgba(75, 192, 192, 1)\",\n                                borderWidth: 1,\n                            },\n                        ],\n                    },\n                    options: {\n                        responsive: true,\n                        maintainAspectRatio: false,\n                        plugins: {\n                            title: {\n                                display: true,\n                                text: \"Top 20 Palabras Clave m\u00e1s Frecuentes\",\n                            },\n                        },\n                        scales: {\n                            y: {\n                                beginAtZero: true,\n                            },\n                        },\n                    },\n                })\n            }\n\n            generateFieldsChart() {\n                const ctx = document.getElementById(\"fieldsChart\").getContext(\"2d\")\n                const sortedFields = Object.entries(this.parsedData.fields).sort((a, b) => b[1] - a[1])\n\n                if (this.charts.fields) {\n                    this.charts.fields.destroy()\n                }\n\n                this.charts.fields = new Chart(ctx, {\n                    type: \"bar\",\n                    data: {\n                        labels: sortedFields.map(([field]) => this.getRISFieldDescription(field)),\n                        datasets: [\n                            {\n                                label: \"Frecuencia\",\n                                data: sortedFields.map(([, count]) => count),\n                                backgroundColor: \"rgba(153, 102, 255, 0.8)\",\n                                borderColor: \"rgba(153, 102, 255, 1)\",\n                                borderWidth: 1,\n                            },\n                        ],\n                    },\n                    options: {\n                        responsive: true,\n                        maintainAspectRatio: false,\n                        plugins: {\n                            title: {\n                                display: true,\n                                text: \"Frecuencia de Campos RIS\",\n                            },\n                        },\n                        scales: {\n                            y: {\n                                beginAtZero: true,\n                            },\n                        },\n                    },\n                })\n            }\n\n            getRISFieldDescription(field) {\n                const descriptions = {\n                    TY: \"Tipo de Documento\",\n                    AU: \"Autor\",\n                    A1: \"Autor Principal\",\n                    A2: \"Autor Secundario\",\n                    TI: \"T\u00edtulo\",\n                    T1: \"T\u00edtulo Principal\",\n                    T2: \"T\u00edtulo Secundario\/Revista\",\n                    JO: \"Revista\",\n                    JF: \"Revista Completa\",\n                    JA: \"Revista Abreviada\",\n                    PY: \"A\u00f1o de Publicaci\u00f3n\",\n                    Y1: \"A\u00f1o Principal\",\n                    AB: \"Resumen\",\n                    KW: \"Palabras Clave\",\n                    DO: \"DOI\",\n                    UR: \"URL\",\n                    VL: \"Volumen\",\n                    IS: \"N\u00famero\",\n                    SP: \"P\u00e1gina Inicial\",\n                    EP: \"P\u00e1gina Final\",\n                    PB: \"Editorial\",\n                    CY: \"Ciudad\",\n                    SN: \"ISSN\/ISBN\",\n                    N1: \"Notas\",\n                    M3: \"Tipo de Medio\",\n                    DB: \"Base de Datos\",\n                    AD: \"Direcci\u00f3n\/Afiliaci\u00f3n\",\n                    C7: \"C\u00f3digo de Art\u00edculo\",\n                    ER: \"Fin de Registro\",\n                }\n                return descriptions[field] || field\n            }\n\n            generateTables() {\n                this.generateAuthorsTable()\n                this.generateYearsTable()\n                this.generateJournalsTable()\n                this.generateTypesTable()\n                this.generateKeywordsTable()\n                this.generateFieldsTable()\n            }\n\n            generateAuthorsTable() {\n                const tbody = document.querySelector(\"#authorsTable tbody\")\n                tbody.innerHTML = \"\"\n\n                const total = this.parsedData.records.length\n                const sortedAuthors = Object.entries(this.parsedData.authors)\n                    .sort((a, b) => b[1] - a[1])\n                    .slice(0, 20)\n\n                sortedAuthors.forEach(([author, count]) => {\n                    const row = tbody.insertRow()\n                    const percentage = ((count \/ total) * 100).toFixed(1)\n\n                    row.insertCell(0).textContent = author\n                    row.insertCell(1).textContent = count\n                    row.insertCell(2).textContent = percentage + \"%\"\n                })\n            }\n\n            generateYearsTable() {\n                const tbody = document.querySelector(\"#yearsTable tbody\")\n                tbody.innerHTML = \"\"\n\n                const total = this.parsedData.records.length\n                const sortedYears = Object.entries(this.parsedData.years).sort((a, b) => b[0] - a[0])\n\n                sortedYears.forEach(([year, count]) => {\n                    const row = tbody.insertRow()\n                    const percentage = ((count \/ total) * 100).toFixed(1)\n\n                    row.insertCell(0).textContent = year\n                    row.insertCell(1).textContent = count\n                    row.insertCell(2).textContent = percentage + \"%\"\n                })\n            }\n\n            generateJournalsTable() {\n                const tbody = document.querySelector(\"#journalsTable tbody\")\n                tbody.innerHTML = \"\"\n\n                const total = Object.values(this.parsedData.journals).reduce((sum, count) => sum + count, 0)\n                const sortedJournals = Object.entries(this.parsedData.journals)\n                    .sort((a, b) => b[1] - a[1])\n                    .slice(0, 20)\n\n                sortedJournals.forEach(([journal, count]) => {\n                    const row = tbody.insertRow()\n                    const percentage = ((count \/ total) * 100).toFixed(1)\n\n                    row.insertCell(0).textContent = journal\n                    row.insertCell(1).textContent = count\n                    row.insertCell(2).textContent = percentage + \"%\"\n                })\n            }\n\n            generateTypesTable() {\n                const tbody = document.querySelector(\"#typesTable tbody\")\n                tbody.innerHTML = \"\"\n\n                const total = this.parsedData.records.length\n                const sortedTypes = Object.entries(this.parsedData.types).sort((a, b) => b[1] - a[1])\n\n                sortedTypes.forEach(([type, count]) => {\n                    const row = tbody.insertRow()\n                    const percentage = ((count \/ total) * 100).toFixed(1)\n\n                    row.insertCell(0).textContent = type\n                    row.insertCell(1).textContent = count\n                    row.insertCell(2).textContent = percentage + \"%\"\n                })\n            }\n\n            generateKeywordsTable() {\n                const tbody = document.querySelector(\"#keywordsTable tbody\")\n                tbody.innerHTML = \"\"\n\n                const total = Object.values(this.parsedData.keywords).reduce((sum, count) => sum + count, 0)\n                const sortedKeywords = Object.entries(this.parsedData.keywords)\n                    .sort((a, b) => b[1] - a[1])\n                    .slice(0, 30)\n\n                sortedKeywords.forEach(([keyword, count]) => {\n                    const row = tbody.insertRow()\n                    const percentage = total > 0 ? ((count \/ total) * 100).toFixed(1) : \"0.0\"\n\n                    row.insertCell(0).textContent = keyword\n                    row.insertCell(1).textContent = count\n                    row.insertCell(2).textContent = percentage + \"%\"\n                })\n            }\n\n            generateFieldsTable() {\n                const tbody = document.querySelector(\"#fieldsTable tbody\")\n                tbody.innerHTML = \"\"\n\n                const total = this.parsedData.records.length\n                const sortedFields = Object.entries(this.parsedData.fields).sort((a, b) => b[1] - a[1])\n\n                sortedFields.forEach(([field, count]) => {\n                    const row = tbody.insertRow()\n                    const percentage = ((count \/ total) * 100).toFixed(1)\n\n                    row.insertCell(0).textContent = field\n                    row.insertCell(1).textContent = this.getRISFieldDescription(field)\n                    row.insertCell(2).textContent = count\n                    row.insertCell(3).textContent = percentage + \"%\"\n                })\n            }\n\n            performSearch() {\n                const searchTerm = document.getElementById(\"searchInput\").value.toLowerCase()\n                const yearFilter = document.getElementById(\"yearFilter\").value\n                const typeFilter = document.getElementById(\"typeFilter\").value\n\n                let filteredRecords = this.parsedData.records\n\n                \/\/ Apply filters\n                if (searchTerm) {\n                    filteredRecords = filteredRecords.filter((record) => {\n                        const title = (record.TI || \"\").toLowerCase()\n                        const authors = this.getRecordAuthors(record).join(\" \").toLowerCase()\n                        const abstract = (record.AB || \"\").toLowerCase()\n\n                        return (\n                            title.includes(searchTerm) ||\n                            authors.includes(searchTerm) ||\n                            abstract.includes(searchTerm)\n                        )\n                    })\n                }\n\n                if (yearFilter) {\n                    filteredRecords = filteredRecords.filter((record) => {\n                        const year = (record.PY || \"\").toString().substring(0, 4)\n                        return year === yearFilter\n                    })\n                }\n\n                if (typeFilter) {\n                    filteredRecords = filteredRecords.filter((record) => {\n                        const type = this.getDocumentTypeDescription(record.TY || \"\")\n                        return type === typeFilter\n                    })\n                }\n\n                this.displaySearchResults(filteredRecords)\n            }\n\n            getRecordAuthors(record) {\n                const authors = []\n                if (record.AU) {\n                    if (Array.isArray(record.AU)) {\n                        authors.push(...record.AU)\n                    } else {\n                        authors.push(record.AU)\n                    }\n                }\n                return authors\n            }\n\n            displaySearchResults(records) {\n                const resultsContainer = document.getElementById(\"searchResults\")\n\n                if (records.length === 0) {\n                    resultsContainer.innerHTML = \"<p>No se encontraron resultados con los filtros aplicados.<\/p>\"\n                    return\n                }\n\n                let html = `<p>Se encontraron ${records.length} resultados:<\/p>`\n\n                records.slice(0, 50).forEach((record) => {\n                    const title = record.TI || \"Sin t\u00edtulo\"\n                    const authors = this.getRecordAuthors(record).join(\", \") || \"Sin autor\"\n                    const year = (record.PY || \"\").toString().substring(0, 4) || \"Sin a\u00f1o\"\n                    const journal = record.T2 || \"Sin revista\"\n                    const type = this.getDocumentTypeDescription(record.TY || \"\")\n\n                    html += `\n                        <div class=\"search-result-item\">\n                            <h4>${title}<\/h4>\n                            <p><strong>Autores:<\/strong> ${authors}<\/p>\n                            <p><strong>A\u00f1o:<\/strong> ${year} | <strong>Tipo:<\/strong> ${type}<\/p>\n                            <p><strong>Revista:<\/strong> ${journal}<\/p>\n                        <\/div>\n                    `\n                })\n\n                if (records.length > 50) {\n                    html += `<p><em>Mostrando los primeros 50 resultados de ${records.length} encontrados.<\/em><\/p>`\n                }\n\n                resultsContainer.innerHTML = html\n            }\n        }\n\n        \/\/ Tab functionality\n        function showTab(tabName) {\n            \/\/ Hide all tab contents\n            document.querySelectorAll(\".tab-content\").forEach((tab) => {\n                tab.classList.remove(\"active\")\n            })\n\n            \/\/ Remove active class from all tab buttons\n            document.querySelectorAll(\".tab-button\").forEach((button) => {\n                button.classList.remove(\"active\")\n            })\n\n            \/\/ Show selected tab content\n            document.getElementById(tabName + \"-tab\").classList.add(\"active\")\n\n            \/\/ Add active class to clicked button\n            event.target.classList.add(\"active\")\n        }\n\n        \/\/ Clear filters function\n        function clearFilters() {\n            document.getElementById(\"searchInput\").value = \"\"\n            document.getElementById(\"yearFilter\").value = \"\"\n            document.getElementById(\"typeFilter\").value = \"\"\n            analyzer.performSearch()\n        }\n\n        \/\/ Export functions\n        function exportToCSV() {\n            const records = analyzer.parsedData.records\n            if (records.length === 0) {\n                alert(\"No hay datos para exportar\")\n                return\n            }\n\n            \/\/ Get all unique fields\n            const allFields = new Set()\n            records.forEach((record) => {\n                Object.keys(record).forEach((field) => allFields.add(field))\n            })\n\n            const fields = Array.from(allFields).sort()\n\n            \/\/ Create CSV content\n            let csv = fields.join(\",\") + \"\\n\"\n\n            records.forEach((record) => {\n                const row = fields.map((field) => {\n                    const value = record[field] || \"\"\n                    const stringValue = Array.isArray(value) ? value.join(\"; \") : value.toString()\n                    return '\"' + stringValue.replace(\/\"\/g, '\"\"') + '\"'\n                })\n                csv += row.join(\",\") + \"\\n\"\n            })\n\n            \/\/ Download CSV\n            const blob = new Blob([csv], { type: \"text\/csv;charset=utf-8;\" })\n            const link = document.createElement(\"a\")\n            link.href = URL.createObjectURL(blob)\n            link.download = \"ris_analysis.csv\"\n            link.click()\n        }\n\n        function exportToJSON() {\n            const data = {\n                records: analyzer.parsedData.records,\n                statistics: {\n                    authors: analyzer.parsedData.authors,\n                    years: analyzer.parsedData.years,\n                    journals: analyzer.parsedData.journals,\n                    types: analyzer.parsedData.types,\n                    keywords: analyzer.parsedData.keywords,\n                    fields: analyzer.parsedData.fields,\n                },\n                summary: {\n                    totalRecords: analyzer.parsedData.records.length,\n                    uniqueAuthors: Object.keys(analyzer.parsedData.authors).length,\n                    uniqueJournals: Object.keys(analyzer.parsedData.journals).length,\n                    yearRange: (() => {\n                        const years = Object.keys(analyzer.parsedData.years)\n                            .map((y) => parseInt(y))\n                            .filter((y) => !isNaN(y))\n                        return years.length > 0 ? `${Math.min(...years)} - ${Math.max(...years)}` : \"-\"\n                    })(),\n                },\n            }\n\n            const blob = new Blob([JSON.stringify(data, null, 2)], { type: \"application\/json\" })\n            const link = document.createElement(\"a\")\n            link.href = URL.createObjectURL(blob)\n            link.download = \"ris_analysis.json\"\n            link.click()\n        }\n\n        function generateReport() {\n            const records = analyzer.parsedData.records\n            if (records.length === 0) {\n                alert(\"No hay datos para generar el reporte\")\n                return\n            }\n\n            const report = `\n# Reporte de An\u00e1lisis Bibliom\u00e9trico\n\n## Resumen Ejecutivo\n- **Total de registros:** ${records.length.toLocaleString()}\n- **Autores \u00fanicos:** ${Object.keys(analyzer.parsedData.authors).length.toLocaleString()}\n- **Revistas \u00fanicas:** ${Object.keys(analyzer.parsedData.journals).length.toLocaleString()}\n- **Rango temporal:** ${(() => {\n                const years = Object.keys(analyzer.parsedData.years)\n                    .map((y) => parseInt(y))\n                    .filter((y) => !isNaN(y))\n                return years.length > 0 ? `${Math.min(...years)} - ${Math.max(...years)}` : \"No disponible\"\n            })()}\n\n## Top 10 Autores m\u00e1s Prol\u00edficos\n${Object.entries(analyzer.parsedData.authors)\n                .sort((a, b) => b[1] - a[1])\n                .slice(0, 10)\n                .map(([author, count], index) => `${index + 1}. ${author}: ${count} publicaciones`)\n                .join(\"\\n\")}\n\n## Top 10 Revistas m\u00e1s Utilizadas\n${Object.entries(analyzer.parsedData.journals)\n                .sort((a, b) => b[1] - a[1])\n                .slice(0, 10)\n                .map(([journal, count], index) => `${index + 1}. ${journal}: ${count} art\u00edculos`)\n                .join(\"\\n\")}\n\n## Distribuci\u00f3n por Tipo de Documento\n${Object.entries(analyzer.parsedData.types)\n                .sort((a, b) => b[1] - a[1])\n                .map(([type, count]) => `- ${type}: ${count} (${((count \/ records.length) * 100).toFixed(1)}%)`)\n                .join(\"\\n\")}\n\n## An\u00e1lisis Temporal\n${Object.entries(analyzer.parsedData.years)\n                .sort((a, b) => b[0] - a[0])\n                .slice(0, 10)\n                .map(([year, count]) => `- ${year}: ${count} publicaciones`)\n                .join(\"\\n\")}\n\n---\nReporte generado el ${new Date().toLocaleDateString(\"es-ES\")} con el Analizador de Archivos RIS\n            `\n\n            const blob = new Blob([report], { type: \"text\/markdown;charset=utf-8;\" })\n            const link = document.createElement(\"a\")\n            link.href = URL.createObjectURL(blob)\n            link.download = \"reporte_bibliometrico.md\"\n            link.click()\n        }\n\n        \/\/ Initialize the analyzer\n        const analyzer = new RISAnalyzer()\n    <\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>Analizador de Archivos RIS &#8211; Herramienta de Investigaci\u00f3n \ud83d\udcca Analizador de Archivos RIS Herramienta avanzada para an\u00e1lisis bibliom\u00e9trico y estad\u00edstico [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_eb_attr":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-2721","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=\/wp\/v2\/pages\/2721","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2721"}],"version-history":[{"count":47,"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=\/wp\/v2\/pages\/2721\/revisions"}],"predecessor-version":[{"id":3153,"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=\/wp\/v2\/pages\/2721\/revisions\/3153"}],"wp:attachment":[{"href":"https:\/\/biblioteca.utc.edu.ec\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2721"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}