<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Analizzatore Accessibilità Testi</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
font-family: 'Inter', sans-serif;
}
.protocol-rules {
max-height: 300px;
overflow-y: auto;
}
/* Custom scrollbar for webkit browsers */
.protocol-rules::-webkit-scrollbar {
width: 8px;
}
.protocol-rules::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
.protocol-rules::-webkit-scrollbar-thumb {
background: #888;
border-radius: 10px;
}
.protocol-rules::-webkit-scrollbar-thumb:hover {
background: #555;
}
.loader {
border: 5px solid #f3f3f3;
border-top: 5px solid #3498db;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
margin: 20px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body class="bg-gray-100 min-h-screen flex flex-col items-center justify-center p-4">

<div class="container mx-auto bg-white p-6 sm:p-8 rounded-xl shadow-2xl w-full max-w-4xl">
<header class="mb-6 text-center">
<h1 class="text-3xl sm:text-4xl font-bold text-gray-800">Analizzatore di Accessibilità dei Testi</h1>
<p class="text-gray-600 mt-2">Inserisci il testo e seleziona un protocollo per l'analisi.</p>
</header>

<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<div>
<label for="inputText" class="block text-sm font-medium text-gray-700 mb-1">Testo Originale:</label>
<textarea id="inputText" rows="10" class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-shadow" placeholder="Incolla qui il tuo testo..."></textarea>
</div>
<div>
<label for="outputText" class="block text-sm font-medium text-gray-700 mb-1">Analisi e Suggerimenti:</label>
<div id="outputText" class="w-full h-64 p-3 border border-gray-300 rounded-lg bg-gray-50 overflow-y-auto shadow-sm">
L'analisi del testo apparirà qui...
</div>
<div id="loader" class="hidden loader"></div>
</div>
</div>

<div class="mb-6">
<h2 class="text-xl font-semibold text-gray-700 mb-3 text-center">Seleziona Protocollo di Analisi:</h2>
<div class="flex flex-col sm:flex-row justify-center space-y-3 sm:space-y-0 sm:space-x-3">
<button id="protocol1Btn" class="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded-lg shadow-md hover:shadow-lg transition-all duration-150 ease-in-out transform hover:scale-105 w-full sm:w-auto">Protocollo Livello 1</button>
<button id="protocol2Btn" class="bg-green-600 hover:bg-green-700 text-white font-semibold py-2 px-4 rounded-lg shadow-md hover:shadow-lg transition-all duration-150 ease-in-out transform hover:scale-105 w-full sm:w-auto">Protocollo Livello 2</button>
<button id="protocol3Btn" class="bg-purple-600 hover:bg-purple-700 text-white font-semibold py-2 px-4 rounded-lg shadow-md hover:shadow-lg transition-all duration-150 ease-in-out transform hover:scale-105 w-full sm:w-auto">Protocollo Livello 3</button>
</div>
</div>

<div id="protocolInfo" class="p-4 border border-gray-200 rounded-lg bg-gray-50 shadow">
<h3 class="text-lg font-semibold text-gray-700 mb-2">Dettagli Protocollo:</h3>
<div id="protocolDetails" class="text-sm text-gray-600 protocol-rules">
Seleziona un protocollo per visualizzare i dettagli.
</div>
</div>
<div id="statusBar" class="mt-4 text-sm text-center text-gray-500"></div>
</div>

<script>
// --- Elementi DOM ---
const inputTextElem = document.getElementById('inputText');
const outputTextElem = document.getElementById('outputText');
const protocolDetailsElem = document.getElementById('protocolDetails');
const protocol1Btn = document.getElementById('protocol1Btn');
const protocol2Btn = document.getElementById('protocol2Btn');
const protocol3Btn = document.getElementById('protocol3Btn');
const loaderElem = document.getElementById('loader');
const statusBarElem = document.getElementById('statusBar');

// --- Dati dei Protocolli (semplificati) ---
const protocols = {
1: {
name: "Livello 1 - Insufficiente competenza linguistica",
rules: [
"Lessico: 80-90% parole del repertorio fondamentale (Vocabolario di Base De Mauro).",
"Stile: Annullamento 'variatio' e uso pleonastico di aggettivi/avverbi.",
"Frasi: < 15 parole. Frasi nucleari complete + modificatori/avverbiali; frasi binucleari coordinate (congiuntive/disgiuntive).",
"Soggetto: Esplicitazione costante.",
"Forme Passive: Assenti.",
"Coesione/Coerenza: Livelli elevati. Mantenimento identità referenza, ordine logico/gerarchico, grammatica storie, esplicitazione obiettivo/motivazione.",
"Riferimenti Enciclopedici: Controllo ed eliminazione processi inferenziali.",
"Veste Grafica: Corpo grande, 80-150 parole/pagina, paragrafi brevi, pagine poco fitte.",
"Immagini: Esplicative analogiche colorate."
],
sentenceLengthMax: 15,
vocabType: "fundamental",
passiveAllowed: false,
subjectExplicit: true,
},
2: {
name: "Livello 2 - Mediocre competenza linguistica",
rules: [
"Lessico: 80-90% parole del repertorio fondamentale e alto uso (Vocabolario di Base).",
"Stile: Parziale annullamento 'variatio' e uso pleonastico.",
"Frasi: < 20 parole. Frasi nucleari + modificatori/avverbiali; frasi binucleari coordinate; frasi binucleari subordinate causali/temporali (esplicite), finali (implicite).",
"Soggetto: Esplicitazione incostante.",
"Forme Passive: Introdotte.",
"Coesione/Coerenza: Livelli medi. Parziale mantenimento identità referenza, ordine logico, grammatica storie, esplicitazione obiettivo/motivazione.",
"Riferimenti Enciclopedici: Controllo e riduzione processi inferenziali.",
"Veste Grafica: Corpo moderato, 150-200 parole/pagina, paragrafi moderati, pagine più ricche.",
"Immagini: Più schematiche, anche B/N."
],
sentenceLengthMax: 20,
vocabType: "high_use", // include fundamental
passiveAllowed: true,
subjectExplicit: null, // incostante
},
3: {
name: "Livello 3 - Quasi sufficiente competenza linguistica",
rules: [
"Lessico: 80-90% parole del repertorio fondamentale, alto uso, alta disponibilità; introduzione parole non VB.",
"Stile: Presenza significativa 'variatio' e uso pleonastico.",
"Frasi: Anche > 20 parole. Introduzione subordinate complesse (consecutive, ipotetiche, concessive, ecc.).",
"Soggetto: Prevalente tendenza a renderlo implicito.",
"Forme Passive: Presenza costante.",
"Coesione/Coerenza: Livelli incostanti.",
"Riferimenti Enciclopedici: Controllo e richiesta di frequenti processi inferenziali.",
"Veste Grafica: Corpo definito da testo originale, 200-250 parole/pagina.",
"Immagini: Possono non essere presenti."
],
sentenceLengthMax: Infinity, // o un limite alto, es. 25-30 per un controllo
vocabType: "high_availability", // include fundamental and high_use
passiveAllowed: true,
subjectExplicit: false,
}
};

// --- Vocabolario di Base De Mauro (Micro-Campione Simulato) ---
// FO: Fondamentale, AU: Alto Uso, AD: Alta Disponibilità
// Per una vera applicazione, questo dovrebbe essere un database esteso.
const deMauroSample = {
'FO': new Set(['la', 'il', 'un', 'di', 'a', 'da', 'in', 'con', 'su', 'per', 'tra', 'e', 'o', 'ma', 'se', 'che', 'non', 'si', 'io', 'tu', 'lui', 'lei', 'noi', 'voi', 'loro', 'essere', 'avere', 'fare', 'dire', 'potere', 'volere', 'sapere', 'stare', 'dovere', 'vedere', 'andare', 'venire', 'dare', 'parlare', 'trovare', 'sentire', 'lasciare', 'prendere', 'guardare', 'mettere', 'pensare', 'passare', 'credere', 'portare', 'diventare', 'sembrare', 'tenere', 'capire', 'morire', 'aprire', 'giocare', 'cominciare', 'scrivere', 'mangiare', 'bere', 'dormire', 'casa', 'uomo', 'donna', 'bambino', 'giorno', 'notte', 'tempo', 'vita', 'morte', 'amore', 'lavoro', 'scuola', 'libro', 'acqua', 'fuoco', 'terra', 'aria', 'sole', 'luna', 'cielo', 'strada', 'mano', 'piede', 'occhio', 'bocca', 'cuore', 'testa', 'grande', 'piccolo', 'bello', 'brutto', 'buono', 'cattivo', 'nuovo', 'vecchio', 'vero', 'falso', 'molto', 'poco', 'sempre', 'mai', 'qui', 'qua', 'lì', 'là', 'sopra', 'sotto', 'dentro', 'fuori', 'prima', 'dopo', 'ancora', 'già', 'forse', 'sì', 'no', 'grazie', 'prego', 'ciao', 'buongiorno', 'sera', 'allora', 'perché', 'quando', 'come', 'dove', 'cosa', 'chi', 'quale', 'quanto', 'padre', 'madre', 'figlio', 'amico']),
'AU': new Set(['albero', 'automobile', 'viaggio', 'musica', 'colore', 'felice', 'triste', 'contento', 'difficile', 'facile', 'importante', 'diverso', 'uguale', 'insieme', 'domani', 'ieri', 'presto', 'tardi', 'spesso', 'qualche', 'nessuno', 'ognuno', 'tutto', 'niente', 'forza', 'paura', 'idea', 'problema', 'risposta', 'domanda', 'esempio', 'inizio', 'fine', 'parte', 'mondo', 'paese', 'città', 'negozio', 'mercato', 'ospedale', 'medico', 'soldi', 'prezzo', 'numero', 'parola', 'frase', 'storia', 'arte', 'natura', 'animale', 'fiore', 'frutto', 'mangiare', 'bere', 'vestito', 'scarpa', 'finestra', 'porta', 'tavolo', 'sedia', 'letto', 'luce', 'rumore', 'silenzio', 'caldo', 'freddo', 'bagnato', 'asciutto', 'pulito', 'sporco', 'aiutare', 'cercare', 'trovare', 'perdere', 'comprare', 'vendere', 'pagare', 'ricevere', 'imparare', 'insegnare', 'capire', 'spiegare', 'ricordare', 'dimenticare', 'sperare', 'desiderare', 'bisogno', 'piacere']),
'AD': new Set(['computer', 'internet', 'telefono', 'cellulare', 'televisione', 'giornale', 'rivista', 'sport', 'cinema', 'teatro', 'ristorante', 'vacanza', 'viaggiare', 'lavorare', 'studiare', 'leggere', 'scrivere', 'ascoltare', 'parlare', 'camminare', 'correre', 'guidare', 'cucinare', 'pulire', 'organizzare', 'progetto', 'obiettivo', 'risultato', 'successo', 'errore', 'opinione', 'decisione', 'informazione', 'comunicazione', 'tecnologia', 'ambiente', 'energia', 'salute', 'economia', 'politica', 'cultura', 'società', 'famiglia', 'relazione', 'sentimento', 'emozione', 'personalità', 'carattere', 'qualità', 'difetto', 'esperienza', 'abitudine', 'cambiamento', 'sviluppo', 'crescita', 'futuro', 'passato', 'presente', 'memoria', 'fantasia', 'creatività', 'innovazione', 'tradizione', 'globale', 'locale', 'digitale', 'virtuale', 'sostenibile'])
};

const getFullVocab = (type) => {
let vocab = new Set([...deMauroSample.FO]);
if (type === "high_use" || type === "high_availability") {
deMauroSample.AU.forEach(word => vocab.add(word));
}
if (type === "high_availability") {
deMauroSample.AD.forEach(word => vocab.add(word));
}
return vocab;
};


// --- Funzioni di Analisi (Semplificate) ---

function splitSentences(text) {
if (!text) return [];
// Regex per dividere per ., !, ?, ma gestendo abbreviazioni comuni (es. Sig., Dott.) e numeri decimali.
// Questa regex è una semplificazione e potrebbe non coprire tutti i casi.
return text.match(/[^.!?]+[.!?]\s*(?![a-zà-ù0-9])/g) || [text];
}

function countWords(sentence) {
if (!sentence) return 0;
return sentence.trim().split(/\s+/).filter(word => word.length > 0).length;
}

function isWordInVocab(word, vocabSet) {
if (!word) return true; // Ignora parole vuote
// Semplice normalizzazione: minuscolo e rimozione punteggiatura base
const normalizedWord = word.toLowerCase().replace(/[.,;:!?()"']/g, '');
return vocabSet.has(normalizedWord);
}

function checkPassiveVoice(sentence) {
// Heuristica molto semplice: cerca "essere" (coniugato) + participio passato
// Forme di essere: è, sono, era, eri, fummo, foste, furono, sarà, sarai, saremo, sarete, saranno, sia, sii, siamo, siate, fossi, fosse, fossimo
// Participi passati spesso finiscono in -ato, -uto, -ito, -so, -to, -esso
const essereForms = /\b(è|e'|sono|era|eri|fu|fummo|foste|furono|sarà|sarai|saremo|sarete|saranno|sia|sii|siamo|siate|fossi|fosse|fossimo|fossero|essere|stato|stata|stati|state)\b/i;
const pastParticiple = /\b\w+(ato|uto|ito|so|to|esso|ata|uta|ita|sa|ta|essa|ati|uti|iti|si|ti|essi|ate|ute|ite|se|te|esse)\b/i;

if (essereForms.test(sentence)) {
const words = sentence.trim().split(/\s+/);
for (let i = 0; i < words.length - 1; i++) {
if (essereForms.test(words[i]) && pastParticiple.test(words[i+1])) {
// Ulteriore controllo: il participio non deve essere preceduto da "aver" o "essere" (per tempi composti attivi)
if (i > 0 && (words[i-1].toLowerCase() === 'aver' || essereForms.test(words[i-1]))) {
// Potrebbe essere un tempo composto attivo, non passivo. Es: "lui è stato bravo"
} else {
return true; // Probabile forma passiva
}
}
}
}
return false;
}

function analyzeText(text, protocolLevel) {
const protocol = protocols[protocolLevel];
const sentences = splitSentences(text);
let analysisResults = [];
let totalWords = 0;
let unknownWordsCount = 0;
const vocabSet = getFullVocab(protocol.vocabType);

if (sentences.length === 0 || (sentences.length === 1 && sentences[0].trim() === '')) {
analysisResults.push({ type: 'info', message: 'Nessun testo da analizzare o testo vuoto.' });
return { analysis: analysisResults, stats: { sentences: 0, words: 0, unknownWords: 0 } };
}

analysisResults.push({ type: 'header', message: `Analisi secondo ${protocol.name}:` });

sentences.forEach((sentence, index) => {
const trimmedSentence = sentence.trim();
if (trimmedSentence === '') return;

const words = trimmedSentence.split(/\s+/).filter(w => w.length > 0);
const numWords = words.length;
totalWords += numWords;

analysisResults.push({ type: 'sentence_header', message: `Frase ${index + 1}: "${trimmedSentence.substring(0,50)}..." (${numWords} parole)` });

// Controllo lunghezza frase
if (protocol.sentenceLengthMax !== Infinity && numWords > protocol.sentenceLengthMax) {
analysisResults.push({ type: 'warning', message: ` - Lunghezza: ${numWords} parole (max consigliato: ${protocol.sentenceLengthMax}). Considera di dividerla o semplificarla.` });
} else {
analysisResults.push({ type: 'ok', message: ` - Lunghezza: ${numWords} parole (OK).` });
}

// Controllo lessico (semplificato)
let sentenceUnknownWords = [];
if (protocol.vocabType) {
words.forEach(word => {
const cleanWord = word.toLowerCase().replace(/[.,;:!?()"']/g, '');
if (cleanWord && !isWordInVocab(cleanWord, vocabSet)) {
sentenceUnknownWords.push(cleanWord);
}
});
if (sentenceUnknownWords.length > 0) {
unknownWordsCount += sentenceUnknownWords.length;
const percentageUnknown = (sentenceUnknownWords.length / numWords * 100).toFixed(1);
analysisResults.push({ type: 'warning', message: ` - Lessico: ${sentenceUnknownWords.length} parole (${percentageUnknown}%) potrebbero non appartenere al repertorio (${protocol.vocabType}): ${sentenceUnknownWords.slice(0,5).join(', ')}${sentenceUnknownWords.length > 5 ? '...' : ''}.` });
} else {
analysisResults.push({ type: 'ok', message: ` - Lessico: Parole sembrano conformi al repertorio ${protocol.vocabType} (controllo semplificato).` });
}
}

// Controllo forma passiva
const hasPassive = checkPassiveVoice(trimmedSentence);
if (hasPassive && !protocol.passiveAllowed) {
analysisResults.push({ type: 'warning', message: ` - Forma Passiva: Rilevata forma passiva (non consentita per Livello 1). Suggerimento: Riformulare in forma attiva.` });
} else if (hasPassive && protocol.passiveAllowed) {
analysisResults.push({ type: 'info', message: ` - Forma Passiva: Rilevata forma passiva (consentita).` });
} else if (!hasPassive && protocolLevel == 3 && protocol.passiveAllowed) { // Livello 3 richiede presenza costante
analysisResults.push({ type: 'info', message: ` - Forma Passiva: Non rilevata (Livello 3 la prevede costante, valuta se appropriato).` });
} else {
analysisResults.push({ type: 'ok', message: ` - Forma Passiva: Non rilevata o non problematica per questo livello.` });
}

// Controllo soggetto esplicito (molto euristico e limitato)
// Questa è una semplificazione estrema. Una vera analisi richiederebbe NLP.
if (protocol.subjectExplicit === true) {
const firstWord = words[0] ? words[0].toLowerCase() : "";
const commonPronouns = ['io', 'tu', 'lui', 'lei', 'egli', 'essa', 'noi', 'voi', 'loro', 'essi', 'esse'];
// Verifica se la frase inizia con un pronome o un articolo (che precede un nome)
const startsWithPronounOrArticle = commonPronouns.includes(firstWord) || ['il', 'lo', 'la', 'i', 'gli', 'le', 'un', 'uno', 'una'].includes(firstWord);
if (!startsWithPronounOrArticle && !/^[A-ZÀ-Ù]/.test(words[0])) { // Non inizia con pronome/articolo e non è un nome proprio (euristica)
analysisResults.push({ type: 'warning', message: ` - Soggetto: Potrebbe mancare un soggetto esplicito. Verificare.` });
} else {
analysisResults.push({ type: 'ok', message: ` - Soggetto: Sembra esplicito (controllo semplificato).` });
}
} else if (protocol.subjectExplicit === false) {
analysisResults.push({ type: 'info', message: ` - Soggetto: Questo livello tende a rendere il soggetto implicito.` });
}


// Altri controlli (es. variatio, pleonasmi) sono troppo complessi per questa demo e vengono omessi.
// Si potrebbero aggiungere note generiche basate sulle regole del protocollo.
if (protocolLevel == 1) {
analysisResults.push({ type: 'info', message: ` - Stile (Liv.1): Ricorda di annullare 'variatio', uso pleonastico di aggettivi/avverbi e processi inferenziali.` });
}
if (protocolLevel == 2) {
analysisResults.push({ type: 'info', message: ` - Stile (Liv.2): Ricorda di ridurre parzialmente 'variatio', uso pleonastico e processi inferenziali.` });
}
if (protocolLevel == 3) {
analysisResults.push({ type: 'info', message: ` - Stile (Liv.3): Questo livello prevede 'variatio', uso pleonastico e processi inferenziali.` });
}


});

analysisResults.push({ type: 'footer', message: `Fine analisi. Totale frasi: ${sentences.filter(s=>s.trim()!=='').length}, Totale parole: ${totalWords}.`});
if (unknownWordsCount > 0) {
analysisResults.push({ type: 'summary_warning', message: `Rilevate ${unknownWordsCount} parole potenzialmente fuori lessico per il livello selezionato (controllo su campione limitato).` });
}

return { analysis: analysisResults, stats: { sentences: sentences.filter(s=>s.trim()!=='').length, words: totalWords, unknownWords: unknownWordsCount } };
}

function displayAnalysis(analysisObj) {
outputTextElem.innerHTML = ''; // Pulisce output precedente
if (analysisObj && analysisObj.analysis) {
analysisObj.analysis.forEach(item => {
const p = document.createElement('p');
p.textContent = item.message;
if (item.type === 'header') p.className = 'font-semibold text-gray-700 mt-2';
else if (item.type === 'sentence_header') p.className = 'font-medium text-gray-600 mt-3 mb-1';
else if (item.type === 'warning') p.className = 'text-red-600 ml-2 text-sm';
else if (item.type === 'ok') p.className = 'text-green-600 ml-2 text-sm';
else if (item.type === 'info') p.className = 'text-blue-600 ml-2 text-sm';
else if (item.type === 'footer') p.className = 'font-semibold text-gray-700 mt-4 border-t pt-2';
else if (item.type === 'summary_warning') p.className = 'text-red-700 font-semibold mt-2';
else p.className = 'text-gray-700 text-sm';
outputTextElem.appendChild(p);
});
} else {
outputTextElem.textContent = 'Nessuna analisi da visualizzare.';
}
}

function displayProtocolInfo(protocolLevel) {
const protocol = protocols[protocolLevel];
protocolDetailsElem.innerHTML = `<h4 class="font-semibold text-md mb-2">${protocol.name}</h4>`;
const ul = document.createElement('ul');
ul.className = 'list-disc list-inside space-y-1';
protocol.rules.forEach(rule => {
const li = document.createElement('li');
li.textContent = rule;
ul.appendChild(li);
});
protocolDetailsElem.appendChild(ul);
}

function handleProtocolSelection(level) {
const text = inputTextElem.value;
if (!text.trim()) {
outputTextElem.innerHTML = '<p class="text-red-500">Per favore, inserisci del testo da analizzare.</p>';
displayProtocolInfo(level); // Mostra comunque le info del protocollo
return;
}

loaderElem.classList.remove('hidden');
outputTextElem.classList.add('hidden');
statusBarElem.textContent = `Analisi in corso per il Livello ${level}...`;


// Simula un piccolo ritardo per l'analisi (per mostrare il loader)
setTimeout(() => {
displayProtocolInfo(level);
const analysisResult = analyzeText(text, level);
displayAnalysis(analysisResult);
loaderElem.classList.add('hidden');
outputTextElem.classList.remove('hidden');
statusBarElem.textContent = `Analisi completata. Frasi: ${analysisResult.stats.sentences}, Parole: ${analysisResult.stats.words}.`;
if (analysisResult.stats.unknownWords > 0) {
statusBarElem.textContent += ` Parole fuori lessico (stima): ${analysisResult.stats.unknownWords}.`;
}
}, 500); // 0.5 secondi di ritardo simulato
}

// --- Event Listeners ---
protocol1Btn.addEventListener('click', () => handleProtocolSelection(1));
protocol2Btn.addEventListener('click', () => handleProtocolSelection(2));
protocol3Btn.addEventListener('click', () => handleProtocolSelection(3));

// Init
protocolDetailsElem.innerHTML = 'Seleziona un protocollo per visualizzare i dettagli e avviare l'analisi.';
</script>

</body>
</html>