MediaWiki:Common.js
Pagina dell'interfaccia di MediaWiki
Altre azioni
Nota: dopo aver pubblicato, potrebbe essere necessario pulire la cache del proprio browser per vedere i cambiamenti.
- Firefox / Safari: tieni premuto il tasto delle maiuscole Shift e fai clic su Ricarica, oppure premi Ctrl-F5 o Ctrl-R (⌘-R su Mac)
- Google Chrome: premi Ctrl-Shift-R (⌘-Shift-R su un Mac)
- Edge: tieni premuto il tasto Ctrl e fai clic su Aggiorna, oppure premi Ctrl-F5.
/* Il codice JavaScript inserito qui viene caricato da ciascuna pagina, per tutti gli utenti. */
$(function() {
$('head').append('<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@900&display=swap" rel="stylesheet">');
var banner = '<div id="custom-banner">' +
'<img src="/images/alephbianco.png" alt="Logo" width="60" height="60" style="flex-shrink:0;object-fit:contain;">' +
'<div class="banner-testo">' +
'<h1>IL CREPUSCOLO DEGLI DÈI</h1>' +
'<p>La più grande Enciclopedia online di Miti e Leggende. In continua evoluzione.</p>' +
'</div>' +
'</div>';
$('.citizen-body-container').before(banner);
});
/* Forza il ricalcolo dei campi condizionali nella Maschera all'apertura */
$(window).on('load', function() {
// Cerchiamo i campi della maschera che potrebbero essere nascosti o vuoti
$('.createboxInput, .pfCombobox, select, input').each(function() {
var $this = $(this);
// Se il campo ha un valore salvato ma è un campo "chiave" (come Sezione o Continente)
if ($this.val() && $this.val() !== '') {
// Un piccolo ritardo di 200ms assicura che Page Forms sia pronto a ricevere il comando
setTimeout(function() {
$this.trigger('change');
}, 200);
}
});
});
$(function() {
if (window.innerWidth <= 768) {
var $scheda = $('table.scheda-singola');
if ($scheda.length) {
var contenuto = $scheda.clone();
$scheda.remove();
$('body').append(
'<div id="scheda-overlay">' +
'<div id="scheda-panel">' +
'<button id="btn-chiudi-scheda">✕</button>' +
contenuto[0].outerHTML +
'</div>' +
'</div>' +
'<button id="btn-apri-scheda"><span>S C H E D A</span></button>'
);
$('#btn-apri-scheda').on('click', function() {
$('#scheda-overlay').addClass('aperta');
});
$('#btn-chiudi-scheda, #scheda-overlay').on('click', function(e) {
if ($(e.target).is('#scheda-overlay') || $(e.target).is('#btn-chiudi-scheda')) {
$('#scheda-overlay').removeClass('aperta');
}
});
}
}
});
// =====================
// EFFETTO SCRAMBLE
// =====================
function scrambleNumero(elemento, valorefinale) {
var durata = 1500;
var intervallo = 50;
var steps = durata / intervallo;
var count = 0;
var timer = setInterval(function() {
count++;
if (count >= steps) {
clearInterval(timer);
elemento.text(valorefinale);
} else {
var progresso = count / steps;
var base = Math.floor(valorefinale * progresso);
var rumore = Math.floor(valorefinale * 0.1 * Math.random());
elemento.text(base + rumore);
}
}, intervallo);
}
// =====================
// CONTATORE CATEGORIE
// =====================
$(document).ready(function() {
function contaPagine(categoria, continueToken, contatoreFinora, elemento) {
var params = {
action: 'query',
list: 'categorymembers',
cmtitle: 'Categoria:' + categoria,
cmlimit: '500',
format: 'json',
origin: '*'
};
if (continueToken) {
params.cmcontinue = continueToken;
}
$.getJSON('https://www.ilcrepuscolodeglidei.org/api.php', params)
.done(function(data) {
var trovati = data.query.categorymembers.length;
var totale = contatoreFinora + trovati;
if (data['continue'] && data['continue']['cmcontinue']) {
contaPagine(categoria, data['continue']['cmcontinue'], totale, elemento);
} else {
scrambleNumero(elemento, totale);
}
})
.fail(function() {
elemento.text('N/D');
});
}
$('.pagesincat-live').each(function() {
var el = $(this);
var categoria = el.data('categoria');
contaPagine(categoria, null, 0, el);
});
});
// =====================
// SOMMA CATEGORIE VOCI
// =====================
$(document).ready(function() {
var categorieDaSommare = [
'Voci'
];
function contaPagineCategoria(categoria) {
var deferred = $.Deferred();
var totale = 0;
function fetch(continueToken) {
var params = {
action: 'query',
list: 'categorymembers',
cmtitle: 'Categoria:' + categoria,
cmlimit: '500',
format: 'json'
};
if (continueToken) params.cmcontinue = continueToken;
$.getJSON('https://www.ilcrepuscolodeglidei.org/api.php', params)
.done(function(data) {
totale += data.query.categorymembers.length;
if (data['continue'] && data['continue']['cmcontinue']) {
fetch(data['continue']['cmcontinue']);
} else {
deferred.resolve(totale);
}
})
.fail(function() { deferred.resolve(0); });
}
fetch(null);
return deferred.promise();
}
if ($('.numberofarticles-live').length) {
var promises = categorieDaSommare.map(function(cat) {
return contaPagineCategoria(cat);
});
$.when.apply($, promises).done(function() {
var args = Array.prototype.slice.call(arguments);
var totaleFinale = args.reduce(function(acc, val) {
return acc + (typeof val === 'number' ? val : val[0]);
}, 0);
scrambleNumero($('.numberofarticles-live'), totaleFinale);
});
}
});
// =====================
// RICERCA INCROCIATA
// =====================
$(function() {
if (!document.getElementById('ricerca-incrociata')) return;
var API = 'https://www.ilcrepuscolodeglidei.org/api.php';
var LIMIT = 100;
// --- NAMESPACE ---
var NS = {
'voci': '0',
'racconti': '516',
'fonti': '518',
'letteratura': '520',
'cinema': '524',
'musica': '522',
'museo': '6'
};
// --- JSON MEDIAWIKI ---
var API_ATU = 'MediaWiki:atu.json';
var API_AT = 'MediaWiki:at-motivi.json';
var API_GEO = 'MediaWiki:geo.json';
var API_SCRITTORI = 'MediaWiki:Scrittori.json';
var API_MUSICISTI = 'MediaWiki:Musicisti.json';
var API_REGISTI = 'MediaWiki:Registi.json';
var API_MUS_AUTORI = 'MediaWiki:MuseoAutori.json';
var API_MUS_UBIC = 'MediaWiki:MuseoUbicazioni.json';
var atuData = null;
var atData = null;
var geoData = null;
var scrittoriData = null;
var musicistiData = null;
var registiData = null;
var museoAutoriData = null;
var museoUbicData = null;
// --- SUBMITS MITOLOGIA ---
var SUBMITS = {
'Mitologia Africana Tribale': ['Mitologia Akamba','Mitologia Efik','Mitologia Odinani','Mitologia Khoi','Mitologia Khoisan','Mitologia Malgascia','Mitologia Bambara','Mitologia Dogon','Mitologia Lugbara','Mitologia Dinka','Mitologia Lotuko','Mitologia Peul','Mitologia Bantu','Mitologia Ashanti','Mitologia Mande','Mitologia Nilotica','Mitologia Nomadica Africana','Mitologia Zulu','Mitologia Kikuyu'],
'Mitologia Afroamericana': ['Mitologia Candomblé','Mitologia Santera','Mitologia Voodoo','Mitologia Fon'],
'Mitologia Artica': ['Mitologia Inuit','Mitologia Lappone','Mitologia Ainu'],
'Mitologia Asiatica Tribale': ['Mitologia Turco-Mongola','Mitologia Indonesiana','Mitologia Malese','Mitologia Filippina','Mitologia Tibetana','Mitologia Kazaka'],
'Mitologia Baltica': ['Mitologia Lituana','Mitologia Lettone','Mitologia Estone'],
'Mitologia Basca': [],
'Mitologia Celtica': ['Mitologia Irlandese','Mitologia Gallese','Mitologia Britannica','Mitologia Gallica'],
'Mitologia Classica': ['Mitologia Greca','Mitologia Romana','Mitologia Etrusca'],
'Mitologia Egizia': [],
'Mitologia Germanica': ['Mitologia Norrena'],
'Mitologia Indo-Iranica': ['Mitologia Indiana','Mitologia Iranica','Mitologia Buddhista','Mitologia Giainista','Mitologia Tamil','Mitologia Armena','Mitologia Osseta','Mitologia Kafir'],
'Mitologia Islamica': ['Mitologia Araba','Mitologia Gnostica'],
'Mitologia Medievale': ['Mitologia Carolingia','Mitologia Cristiana'],
'Mitologia Mesopotamica': ['Mitologia Sumera','Mitologia Babilonese','Mitologia Ittita','Mitologia Urrita'],
'Mitologia Oceanica': ['Mitologia Aborigena','Mitologia Maori','Mitologia Polinesiana','Mitologia Melanesiana','Mitologia Micronesiana'],
'Mitologia Orientale': ['Mitologia Cinese','Mitologia Giapponese','Mitologia Coreana','Mitologia Taoista'],
'Mitologia Pellerossa': ['Mitologia Algonchina','Mitologia Irochese','Mitologia Sioux','Mitologia Nadene','Mitologia Pueblo','Mitologia Muskogi','Mitologia Wakashan','Mitologia Penuti','Mitologia Hokan','Mitologia Caddo','Mitologia Cherokee','Mitologia Dakota','Mitologia Lakota','Mitologia Navaho','Mitologia Ojibwa','Mitologia Haida','Mitologia Kwakiutl','Mitologia Tlingit','Mitologia Salish','Mitologia Crow','Mitologia Arikara','Mitologia Pawnee','Mitologia Seneca','Mitologia Mohawk','Mitologia Onondaga','Mitologia Uroni','Mitologia Micmac','Mitologia Lenape','Mitologia Maliseet','Mitologia Passamaquoddy','Mitologia Piedi Neri','Mitologia Creek','Mitologia Cheyenne','Mitologia Arapaho','Mitologia Kiowa'],
'Mitologia Precolombiana': ['Mitologia Azteca','Mitologia Maya','Mitologia Inca','Mitologia Amazzonica','Mitologia Guarani','Mitologia Chamacoco'],
'Mitologia Semitica': ['Mitologia Ebraica','Mitologia Fenicia'],
'Mitologia Slava': [],
'Mitologia Ugro-Finnica': ['Mitologia Finnica','Mitologia Magiara']
};
// --- AREE E GEO (solo fino a Paese) ---
var AREE = {
'Europa': ['Nordeuropa','Europa dell\'Est','Europa Centrale','Europa Occidentale','Mediterraneo'],
'Africa': ['Africa del Nord','Africa Occidentale','Africa Centrale','Africa Orientale','Africa del Sud'],
'Asia': ['Asia Settentrionale','Asia Centrale','Asia Meridionale','Medio Oriente','Estremo Oriente','Sudest Asiatico'],
'Americhe': ['Nordamerica','Centroamerica','Sudamerica'],
'Oceania': ['Oceania Vicina','Oceania Lontana'],
'Poli': ['Artico','Antartico']
};
var GEO = {
'Europa': {
'Mediterraneo': {
'Grecia': {}, 'Italia': {}
},
'Europa Occidentale': {
'Belgio': {}, 'Francia': {}, 'Lussemburgo': {},
'Olanda': {}, 'Portogallo': {}, 'Spagna': {}
},
'Nordeuropa': {
'Galles': {}, 'Inghilterra': {},
'Irlanda': {}, 'Irlanda del Nord': {}, 'Norvegia': {}, 'Scozia': {}, 'Svezia': {}
},
'Europa dell\'Est': {
'Russia': {}, 'Polonia': {}
},
'Europa Centrale': {
'Germania': {}, 'Austria': {}
}
},
'Africa': {
'Africa del Nord': {
'Egitto': {}, 'Marocco': {}
},
'Africa Occidentale': {
'Nigeria': {}, 'Ghana': {}
},
'Africa Centrale': {
'Congo': {}, 'Camerun': {}
},
'Africa Orientale': {
'Kenya': {}, 'Etiopia': {}
},
'Africa del Sud': {
'Sudafrica': {}, 'Zimbabwe': {}
}
},
'Asia': {
'Medio Oriente': {
'Mesopotamia': {}, 'Persia': {}
},
'Estremo Oriente': {
'Cina': {}, 'Giappone': {}
},
'Asia Settentrionale': {
'Siberia': {}, 'Mongolia': {}
},
'Asia Centrale': {
'Kazakhstan': {}, 'Uzbekistan': {}
},
'Asia Meridionale': {
'India': {}, 'Nepal': {}
},
'Sudest Asiatico': {
'Thailandia': {}, 'Indonesia': {}
}
},
'Americhe': {
'Nordamerica': {
'Usa': {}, 'Canada': {}
},
'Centroamerica': {
'Messico': {}, 'Guatemala': {}
},
'Sudamerica': {
'Brasile': {}, 'Perù': {}
}
},
'Oceania': {
'Oceania Vicina': {
'Australia': {}, 'Nuova Zelanda': {}
},
'Oceania Lontana': {
'Hawaii': {}, 'Polinesia': {}
}
},
'Poli': {
'Artico': {
'Groenlandia': {}, 'Alaska': {}
},
'Antartico': {
'Antartide': {}, 'Isole Sub-Antartiche': {}
}
}
};
// --- TIPOLOGIA CASCATA ---
var SOTTOTIPO = {
'Divinità': ['Divinità delle Arti','Divinità delle Astrazioni','Divinità delle Attività','Divinità Infere','Divinità della Natura','Divinità Superne','Divinità Tutelari','Epiteti'],
'Semidèi': ['Eroi','Santi','Epiteti'],
'Creature Fantastiche': ['Animali','Esseri Intermediari','Mutanti','Non Morti','Piccolo Popolo','Umanoidi','Vegetali','Epiteti'],
'Umani': ['Artigiani','Artisti','Fattori','Guerrieri','Istituzioni','Malviventi','Nobili','Popoli','Religiosi','Scienziati','Servitori','Epiteti'],
'Luoghi': ['Costruzioni','Luoghi Naturali','Luoghi Soprannaturali'],
'Oggetti': ['Alimenti','Armi','Corpi Celesti','Gioielli','Recipienti','Strumenti Musicali','Veicoli'],
'Eventi': ['Calendario','Eventi Cosmici','Riti']
};
var SPECIFICITA = {
'Divinità delle Arti': ['Divinità del Canto','Divinità della Danza','Divinità della Musica','Divinità della Poesia','Divinità della Scrittura'],
'Divinità delle Astrazioni': ["Divinità dell'Amicizia","Divinità dell'Amore","Divinità dell'Apostasia",'Divinità della Conoscenza','Divinità del Coraggio','Divinità della Fame','Divinità del Fato','Divinità della Felicità','Divinità della Fortuna','Divinità della Forza','Divinità della Gelosia','Divinità dei Giuramenti','Divinità della Giustizia',"Divinità dell'Immortalità","Divinità dell'Inganno","Divinità dell'Invidia",'Divinità della Longevità','Divinità delle Malattie','Divinità del Male','Divinità del Matrimonio',"Divinità dell'Odio","Divinità dell'Onore",'Divinità della Parola','Divinità della Pazzia','Divinità del Pensiero','Divinità del Piacere','Divinità della Pietà','Divinità della Povertà','Divinità della Prudenza','Divinità della Ricchezza','Divinità della Saggezza','Divinità della Salute','Divinità del Silenzio','Divinità del Sonno','Divinità della Speranza','Divinità dei Tabù','Divinità del Tempo','Divinità della Vecchiaia','Divinità della Vendetta','Divinità della Violenza','Divinità della Vittoria'],
'Divinità delle Attività': ["Divinità dell'Agricoltura",'Divinità degli Artigiani','Divinità del Commercio','Divinità della Caccia','Divinità della Guerra','Divinità della Magia','Divinità della Medicina','Divinità della Pesca','Divinità del Sesso','Divinità del Viaggio'],
'Divinità Infere': ['Divinità della Morte',"Divinità dell'Oltretomba"],
'Divinità della Natura': ["Divinità dell'Acqua",'Divinità degli Alimenti','Divinità delle Alluvioni','Divinità degli Animali','Divinità della Fertilità','Divinità dei Fiumi','Divinità del Fuoco','Divinità del Mare','Divinità della Montagna','Divinità delle Stagioni','Divinità della Terra','Divinità dei Terremoti','Divinità della Vegetazione','Divinità dei Vulcani'],
'Divinità Superne': ["Divinità dell'Alba","Divinità dell'Arcobaleno",'Divinità del Cielo','Divinità della Creazione','Divinità della Grandine','Divinità delle Eclissi','Divinità della Luce','Divinità della Luna','Divinità della Neve','Divinità della Notte','Divinità dei Pianeti','Divinità della Pioggia','Divinità del Sole','Divinità delle Stelle','Divinità della Tempesta','Divinità del Tuono','Divinità del Vento'],
'Divinità Tutelari': ['Divinità della Famiglia','Divinità della Nascita','Divinità dei Punti Cardinali','Divinità delle Strade','Divinità dei Villaggi'],
'Eroi': ['Eroi Classici','Eroi Culturali','Trickster'],
'Animali': ['Anfibi','Aracnidi','Bovini','Canidi','Caprini','Cetacei','Cervidi','Crostacei','Equini','Felini','Insetti','Leporidi','Molluschi','Pachidermi','Pesci','Pipistrelli','Primati','Rettili','Rinocerontidi','Roditori','Suini','Uccelli'],
'Esseri Intermediari': ['Angeli','Demoni','Elementali'],
'Mutanti': ['Mannari','Proteiformi'],
'Non Morti': ['Fantasmi','Vampiri','Zombi'],
'Piccolo Popolo': ['Elfi','Fate','Folletti','Gnomi','Nani'],
'Umanoidi': ['Alieni','Automi','Giganti','Maghi&Streghe','Orchi','Popoli Favolosi','Uomini Selvaggi'],
'Vegetali': ['Piante'],
'Epiteti': ['Epiteti Individuali','Epiteti Collettivi'],
'Artigiani': ['Fabbri'],
'Artisti': ['Musici','Poeti','Scultori'],
'Fattori': ['Contadini','Pastori'],
'Guerrieri': ['Araldi','Arcieri','Aurighi','Cavalieri','Condottieri','Fanti','Scudieri'],
'Istituzioni': ['Funzionari','Governatori'],
'Malviventi': ['Briganti','Ladri','Pirati'],
'Nobili': ['Baroni','Conti','Duchi','Principi','Sovrani'],
'Religiosi': ['Druidi','Profeti','Sacerdoti','Sciamani'],
'Servitori': ['Ancelle']
};
var tematiche = ['Abbandono','Accecamento','Acqua','Adulterio','Agricoltura','Alba','Alchimia','Alfabeto','Alimenti','Alluvioni','Amicizia','Amore','Amori','Amputazione','Antropofagia','Apostasia','Arcobaleno','Aria','Arroganza','Arte','Artigianato','Autolesionismo','Autunno','Avvelenamento','Bellezza','Bene','Bestiame','Caccia','Calendario','Calma','Cannibalismo','Canto','Caos','Castità','Cielo','Ciclopia','Circoncisione','Codardia','Commercio','Condanna a Morte','Contraffazione','Conversione','Coraggio','Costellazioni','Creazione','Crepuscolo','Danza','Decapitazione','Denaro','Diluvio Universale','Dono','Ebbrezza','Eclissi','Eco','Eponimia','Eruzioni','Esilio','Estate','Evirazione','Fame','Famiglia','Fato','Felicità','Fertilità','Figlicidio','Fine del Mondo','Fortuna','Forza','Fratricidio','Fulmini','Fuoco','Furbizia','Furto','Futuro','Gelosia','Gemelli','Gigantismo','Giorno','Giovinezza','Giustizia','Grandine','Gratitudine','Guerra','Immortalità','Incesto','Incubi','Infanticidio','Infertilità','Inganno','Ingordigia','Intelligenza','Inverno','Invidia','Invulnerabilità','Ira','Kalokagathia','Legge','Letteratura','Longevità','Luce','Luna','Magia','Malattie','Male','Maledizione','Mare','Maree','Matricidio','Matrimonio','Medicina','Memoria','Menzogna','Metamorfosi','Mimetismo','Miracoli','Miraggi','Montagna','Morte','Musica','Nascita','Natura','Nebbia','Neve','Notte','Nuvole','Odio','Omicidio','Omosessualità','Oltretomba','Ombra','Onde','Onore','Pace','Parricidio','Parto','Passato','Patto','Paura','Pazzia','Peccato Originale','Pesca','Peste','Piacere','Pianeti','Pietà','Pigrizia','Pioggia','Pittura','Poesia','Policefalia','Povertà','Presagio','Presente','Primavera','Prostituzione','Protezione','Prudenza','Punizione Divina','Punti Cardinali','Purezza','Rapimento','Reincarnazione','Resurrezione','Ricchezza','Ricerca','Ricordo','Ritualità','Rugiada','Sacrificio','Saggezza','Salute','Sangue','Sapienza','Schiavitù','Scrittura','Seduzione','Sessualità','Siccità','Silenzio','Sodomia','Sogno','Sole','Sonno','Speranza','Stagioni','Stelle','Strade','Stupro','Sud','Suicidio','Superbia','Tabù','Tempeste','Tempo','Tempo Atmosferico','Terra','Terremoti','Tesori','Tornado','Tradimento','Tuoni','Usurpazione','Uxoricidio','Vecchiaia','Vegetazione','Velocità','Vendetta','Vento','Verginità','Viaggio','Violenza','Virilità','Vita','Vittoria'];
// =====================
// HELPER: campo select generico
// =====================
function campo(id, label, opzioni, prefix) {
prefix = prefix || 'ri-';
var tutti = (label === 'Indole' || label === 'Tipologia' || label === 'Tematiche' || label === 'Attributi armi' || label === 'Attributi abbigliamento' || label === 'Macrosezione' || label === 'Altre sezioni') ? 'tutte' : 'tutti';
var opts = '<option value="">— ' + tutti + ' —</option>';
opzioni.forEach(function(o) { opts += '<option>' + o + '</option>'; });
return '<div><label style="font-size:15px;display:block;margin-bottom:4px">' + label + '</label>' +
'<select id="' + prefix + id + '" style="width:100%;font-size:15px">' + opts + '</select></div>';
}
// =====================
// HELPER: gruppo geografico
// =====================
function geoBlock(prefix) {
return '<div><label style="font-size:15px;display:block;margin-bottom:4px">Continente</label>' +
'<select id="' + prefix + 'continente" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'<option value="Europa">Europa</option><option value="Africa">Africa</option>' +
'<option value="Asia">Asia</option><option value="Americhe">Americhe</option>' +
'<option value="Oceania">Oceania</option><option value="Poli">Poli</option>' +
'</select>' +
'<div id="' + prefix + 'area-sub" style="display:none;margin-top:6px"><label style="font-size:15px;display:block;margin-bottom:4px">Area</label><select id="' + prefix + 'area" style="width:100%;font-size:15px"><option value="">— tutte —</option></select></div>' +
'<div id="' + prefix + 'paese-sub" style="display:none;margin-top:6px"><label style="font-size:15px;display:block;margin-bottom:4px">Paese</label><select id="' + prefix + 'paese" style="width:100%;font-size:15px"><option value="">— tutti —</option></select></div>' +
'<div id="' + prefix + 'regione-sub" style="display:none;margin-top:6px"><label style="font-size:15px;display:block;margin-bottom:4px">Regione</label><select id="' + prefix + 'regione" style="width:100%;font-size:15px"><option value="">— tutte —</option></select></div>' +
'<div id="' + prefix + 'provincia-sub" style="display:none;margin-top:6px"><label style="font-size:15px;display:block;margin-bottom:4px">Provincia</label><select id="' + prefix + 'provincia" style="width:100%;font-size:15px"><option value="">— tutte —</option></select></div>' +
'<div id="' + prefix + 'citta-sub" style="display:none;margin-top:6px"><label style="font-size:15px;display:block;margin-bottom:4px">Città</label><select id="' + prefix + 'citta" style="width:100%;font-size:15px"><option value="">— tutte —</option></select></div>' +
'</div>';
}
// =====================
// GESTIONE ANNO / EPOCHE
// =====================
var ROMANI = { I:1,II:2,III:3,IV:4,V:5,VI:6,VII:7,VIII:8,IX:9,X:10,
XI:11,XII:12,XIII:13,XIV:14,XV:15,XVI:16,XVII:17,XVIII:18,XIX:19,XX:20,XXI:21 };
function parseAnno(str) {
if (!str) return null;
str = str.trim().replace(/\s+/g, ' ');
var aC = /a\.?\s*[cC]\.?/.test(str);
var mRange = str.match(/^([IVXLCDM]+)[–\-]([IVXLCDM]+)\s+sec/i);
if (mRange) {
var n1 = ROMANI[mRange[1].toUpperCase()];
var n2 = ROMANI[mRange[2].toUpperCase()];
if (n1 && n2) {
var c1 = (n1 - 1) * 100 + 50;
var c2 = (n2 - 1) * 100 + 50;
var mid = Math.round((c1 + c2) / 2);
return aC ? -mid : mid;
}
}
var mSec = str.match(/([IVXLCDM]+)\s+sec/i);
if (mSec) {
var n = ROMANI[mSec[1].toUpperCase()];
if (n) {
var centro = (n - 1) * 100 + 50;
return aC ? -centro : centro;
}
}
var mNum = str.match(/(-?\d+)/);
if (mNum) {
var anno = parseInt(mNum[1], 10);
return aC ? -Math.abs(anno) : anno;
}
return null;
}
var EPOCHE = [
{ label: '— tutte —', min: null, max: null },
{ label: 'Antichità (fino al 476)', min: -99999, max: 476 },
{ label: 'Medioevo (477–1499)', min: 477, max: 1499 },
{ label: 'Rinascimento/Età Moderna (1500–1799)', min: 1500, max: 1799 },
{ label: 'Ottocento (1800–1899)', min: 1800, max: 1899 },
{ label: 'Novecento (1900–1999)', min: 1900, max: 1999 },
{ label: 'Contemporaneo (dal 2000)', min: 2000, max: 99999 }
];
var EPOCHE_CINEMA = [
{ label: '— tutte —', min: null, max: null },
{ label: 'Cinema Muto (fino al 1929)', min: -99999, max: 1929 },
{ label: 'Epoca d\'Oro (1930–1959)', min: 1930, max: 1959 },
{ label: 'Nuovo Cinema (1960–1979)', min: 1960, max: 1979 },
{ label: 'Blockbuster Era (1980–1999)', min: 1980, max: 1999 },
{ label: 'Contemporaneo (dal 2000)', min: 2000, max: 99999 }
];
function epocaSelectHtml(id, epoche) {
var list = epoche || EPOCHE;
var s = '<select id="' + id + '" style="width:100%;font-size:15px">';
list.forEach(function(e, i) { s += '<option value="' + i + '">' + e.label + '</option>'; });
s += '</select>';
return s;
}
function annoInEpoca(cats, epocaIdx, epoche) {
if (!epocaIdx || epocaIdx === '0') return true;
var list = epoche || EPOCHE;
var ep = list[parseInt(epocaIdx, 10)];
if (!ep || ep.min === null) return true;
for (var i = 0; i < cats.length; i++) {
if (cats[i].indexOf('Anno: ') === 0) {
var raw = cats[i].replace('Anno: ', '');
var anno = parseAnno(raw);
if (anno !== null && anno >= ep.min && anno <= ep.max) return true;
}
}
return false;
}
// =====================
// COSTRUZIONE HTML
// =====================
var optsT = '<option value="">— tutte —</option>';
tematiche.forEach(function(t) { optsT += '<option>' + t + '</option>'; });
var html = '<div style="padding:1rem 0">';
html += '<p style="font-size:18px;font-weight:500;margin-bottom:1.5rem">Ricerca incrociata</p>';
html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;margin-bottom:1.5rem">';
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Aree Tematiche</label>' +
'<select id="ri-area-tematica" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="voci">Voci</option>' +
'<option value="racconti">Racconti</option>' +
'<option value="fonti">Fonti</option>' +
'<option value="letteratura">Letteratura</option>' +
'<option value="cinema">Cinema</option>' +
'<option value="museo">Museo</option>' +
'<option value="musica">Musica</option>' +
'</select></div>';
html += '<div id="ri-sezione-wrapper" style="display:none">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Macrosezione</label>' +
'<select id="ri-macrosezione" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Mitologia">Mitologia</option>' +
'<option value="Folklore">Folklore</option>' +
'<option value="Mitologia & Folklore">Mitologia & Folklore</option>' +
'</select>' +
'<div id="ri-mit-wrapper" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sezione</label>' +
'<select id="ri-mit-macro" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Mitologia Africana Tribale">Africana Tribale</option>' +
'<option value="Mitologia Afroamericana">Afroamericana</option>' +
'<option value="Mitologia Artica">Artica</option>' +
'<option value="Mitologia Asiatica Tribale">Asiatica Tribale</option>' +
'<option value="Mitologia Baltica">Baltica</option>' +
'<option value="Mitologia Basca">Basca</option>' +
'<option value="Mitologia Celtica">Celtica</option>' +
'<option value="Mitologia Classica">Classica</option>' +
'<option value="Mitologia Egizia">Egizia</option>' +
'<option value="Mitologia Germanica">Germanica</option>' +
'<option value="Mitologia Indo-Iranica">Indo-Iranica</option>' +
'<option value="Mitologia Islamica">Islamica</option>' +
'<option value="Mitologia Medievale">Medievale</option>' +
'<option value="Mitologia Mesopotamica">Mesopotamica</option>' +
'<option value="Mitologia Oceanica">Oceanica</option>' +
'<option value="Mitologia Orientale">Orientale</option>' +
'<option value="Mitologia Pellerossa">Pellerossa</option>' +
'<option value="Mitologia Precolombiana">Precolombiana</option>' +
'<option value="Mitologia Semitica">Semitica</option>' +
'<option value="Mitologia Slava">Slava</option>' +
'<option value="Mitologia Ugro-Finnica">Ugro-Finnica</option>' +
'</select>' +
'<div id="ri-mit-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sottosezione</label>' +
'<select id="ri-mit-specific" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div></div>' +
'<div id="ri-folk-wrapper" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sezione</label>' +
'<select id="ri-folk-sezione" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Creature Fantastiche">Creature Fantastiche</option>' +
'<option value="Itinerari Arcani">Itinerari Arcani</option>' +
'<option value="Misteri">Misteri</option>' +
'<option value="Simboli">Simboli</option>' +
'</select></div>' +
'</div>';
html += '<div id="ri-tipo-wrapper" style="display:none">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Tipologia</label>' +
'<select id="ri-tipo" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option>Divinità</option><option>Semidèi</option><option>Creature Fantastiche</option>' +
'<option>Umani</option><option>Luoghi</option><option>Oggetti</option>' +
'<option>Eventi</option><option>Concetti</option>' +
'</select>' +
'<div id="ri-sottotipo-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sottotipologia</label>' +
'<select id="ri-sottotipo" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div>' +
'<div id="ri-specificita-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Specificità</label>' +
'<select id="ri-specificita" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div>' +
'</div>';
html += '<div id="ri-continente-wrapper" style="display:none">' + geoBlock('ri-') + '</div>';
html += '</div>';
// PANNELLO VOCI
html += '<div id="ri-voci-panel" style="display:none">';
html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;margin-bottom:1.5rem">';
html += campo('sesso', 'Sesso', ['Maschio','Femmina','Neutro','Androgino']);
html += campo('aspetto', 'Aspetto', ['Antropomorfo','Animale','Animale-Animale','Animale-Minerale','Animale-Vegetale','Uomo-Animale','Uomo-Minerale','Uomo-Vegetale','Informe','Mutante','Minerale','Vegetale']);
html += campo('indole', 'Indole', ['Benevola','Malevola','Neutrale']);
html += campo('elemento', 'Elemento', ['Acqua','Aria','Fuoco','Terra']);
html += campo('habitat', 'Habitat', ['Alberi','Campagna','Casa','Castello','Cielo','Cimitero','Città','Collina','Deserto','Fiume','Foresta','Ghiacci','Giungla','Grotta','Inferi','Lago','Mare','Montagna','Palude','Paradiso','Pozzo','Rovine','Savana','Sottosuolo','Strade','Terra','Vulcano']);
html += campo('fisici', 'Attributi fisici', ['Ali','Articolazioni (assenza)','Artigli','Baffi','Barba','Becco','Bocca (più di una)','Calvizie','Capelli','Capelli biondi','Capelli lunghi','Capelli rossi','Cecità','Ciclopia','Coda','Coda (più di una)','Corna','Corno','Denti a sciabola','Edentulismo','Fallo','Forza Sovrumana','Gamba (una)','Gigantismo','Glabrismo','Labbra procunciate','Lingua (biforcuta)','Macrocefalia','Mani','Nanismo','Narice (una)','Naso pronunciato','Occhi azzurri','Occhi grandi','Occhi sul dorso','Ombra (assenza)','Ombra diversa','Orecchie (piccole)','Orecchie (più di due)','Pancia prominente','Pelle (dura)','Pelle blu','Penne taglienti','Piaghe','Piume','Poliartria','Policefalia','Polimelia','Polioftalmia','Profumo','Scheletro','Seni (più di due)','Seni pendenti','Seni pronunciati','Sopracciglia','Sterco','Terioantropia','Teriocefalia','Teriomorfismo','Testicoli (grandi)','Vagina','Voce (roca)']);
html += campo('abbigl', 'Attributi abbigliamento', ['Amuleto','Anello','Bastone','Borsa','Cappello','Cappuccio','Cintura','Collana','Ghirlanda','Mantello','Occhiali','Scarpe','Scialle','Velo']);
html += campo('armi', 'Attributi armi', ['Alabarda','Arco','Ascia','Boomerang','Coltello','Elmo','Freccia','Lancia','Machete','Martello','Mazza','Pugnale','Scudo','Spada','Tridente']);
html += campo('animali', 'Attributi animali', ['Agnello','Airone','Anatra','Anguilla','Antilope','Ape','Aquila','Asino','Avvoltoio','Baco da seta','Balena','Barbagianni','Bradipo','Bue','Cammello','Camoscio','Cane','Capra','Capriolo','Cardellino','Carpa','Castoro','Cavalletta','Cavallo','Cervo','Cicala','Cicogna','Cigno','Cinghiale','Civetta','Cobra','Coccodrillo','Colomba','Coniglio','Cornacchia','Corvo','Coyote','Cuculo','Daino','Delfino','Drago','Elefante','Emù','Fagiano','Falco','Farfalla','Foca','Formica','Gallina','Gallo','Gatto','Gazza','Granchio','Gru','Gufo','Ibis','Iena','Insetto','Ippocampo','Ippopotamo','Leone','Leopardo','Lepre','Lince','Lontra','Lucertola','Lumaca','Lupo','Maiale','Martora','Montone','Mosca','Nibbio','Oca','Orso','Pantera','Pappagallo','Pavone','Pecora','Pesce','Picchio','Pipistrello','Polpo','Ragno','Ramarro','Rana','Renna','Rondine','Rospo','Sciacallo','Scimmia','Serpente','Squalo','Stambecco','Tartaruga','Tigre','Toro','Tricheco','Uccello','Vacca','Verme','Zibetto']);
html += campo('minerali', 'Attributi minerali', ['Argento','Avorio','Bronzo','Ferro','Oro','Perla','Pietra','Rame','Rubino','Smeraldo']);
html += campo('vegetali', 'Attributi vegetali', ['Acacia','Acanto','Acero','Alberi','Bambù','Betulla','Canna','Cetriolo','Cipresso','Fiori','Giacinto','Giglio','Grano','Loto','Maggiorana','Mais','Mela','Miglio','Ninfea','Palma','Pesca','Pino','Pioppo','Rosa','Samong','Tiglio','Ulivo']);
html += campo('colori', 'Attributi colori', ['Azzurro','Bianco','Blu','Giallo','Grigio','Nero','Rosso','Tutti i colori','Verde']);
html += campo('numeri', 'Attributi numeri', ['0','1','2','3','4','5','6','7','8','9','10','11','12','13']);
html += campo('altri', 'Attributi altri oggetti', ['Ancora','Aratro','Armonica','Bastone','Caduceo','Candela','Catena','Cembalo','Cesto','Cetra','Conchiglia','Corda','Cornucopia','Corona','Croce','Disco lunare','Disco solare','Flauto','Incenso','Incudine','Libro','Martello','Mezzaluna','Moneta','Pettine','Respiro','Sacca','Sasso','Secchio','Specchio','Stendardo','Teschio','Torcia','Uovo','Vanga','Vaso']);
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Tematiche</label><select id="ri-tematiche" style="width:100%;font-size:15px">' + optsT + '</select></div>';
html += '</div></div>';
// PANNELLO RACCONTI
html += '<div id="ri-racconti-panel" style="display:none">';
html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;margin-bottom:1.5rem">';
html += geoBlock('rac-');
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Autore</label>' +
'<select id="rac-autore" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'<option>Andersen, Hans Christian</option>' +
'<option>Grimm, Jacob & Wilhelm</option>' +
'<option>La Fontaine, Jean de la</option>' +
'</select></div>';
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Tipologia</label>' +
'<select id="rac-tipologia" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option>Racconti Popolari</option><option>Favole</option>' +
'<option>Fiabe</option><option>Leggende</option><option>Miti</option>' +
'</select></div>';
html += '<div>' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Classificazione ATU - Macrogruppo</label>' +
'<select id="rac-atu-macro" style="width:100%;font-size:15px"><option value="">— tutti —</option></select>' +
'<div id="rac-atu-gruppo-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Gruppo</label>' +
'<select id="rac-atu-gruppo" style="width:100%;font-size:15px"><option value="">— tutti —</option></select>' +
'</div>' +
'<div id="rac-atu-numero-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Numero</label>' +
'<select id="rac-atu-numero" style="width:100%;font-size:15px"><option value="">— tutti —</option></select>' +
'</div></div>';
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Motivi AT <span style="color:var(--color-text-secondary);font-size:13px">(Ctrl+clic per più valori)</span></label>' +
'<select id="rac-at-motivi" multiple style="width:100%;font-size:15px;height:120px"><option disabled>Caricamento...</option></select></div>';
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Tematiche</label><select id="rac-tematiche" style="width:100%;font-size:15px">' + optsT + '</select></div>';
html += '</div></div>';
// PANNELLO FONTI
html += '<div id="ri-fonti-panel" style="display:none">';
html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;margin-bottom:1.5rem">';
// Autore (popolato da MediaWiki:autori-fonti.json)
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Autore</label>' +
'<select id="fon-autore" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'</select></div>';
// Tipo
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Tipo</label>' +
'<select id="fon-tipo" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'<option>Fonti Antiche</option><option>Fonti Moderne</option>' +
'</select></div>';
// Forma
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Forma</label>' +
'<select id="fon-forma" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option>Annali/Opere Storiche</option><option>Canti/Inni/Poesie</option>' +
'<option>Codici/Libri Illustrati</option><option>Dizionari/Enciclopedie</option>' +
'<option>Mitografie</option><option>Opere Epiche</option>' +
'<option>Opere Naturalistiche</option><option>Peripli/Opere Geografiche</option>' +
'<option>Saggi</option><option>Testi Sacri</option><option>Tragedie</option>' +
'</select></div>';
// In Biblioteca
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">In Biblioteca</label>' +
'<select id="fon-biblioteca" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'<option value="Biblioteca">Sì</option>' +
'</select></div>';
// Epoca / Datazione
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Epoca</label>' +
epocaSelectHtml('fon-epoca') + '</div>';
// Blocco geografico (Continente → Area → Paese)
html += geoBlock('fon-');
// Blocco sezioni (Macrosezione → Sezione → Sottosezione)
html += '<div>' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Macrosezione</label>' +
'<select id="fon-macrosezione" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Mitologia">Mitologia</option>' +
'<option value="Folklore">Folklore</option>' +
'</select>' +
'<div id="fon-mit-wrapper" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sezione</label>' +
'<select id="fon-mit-macro" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Mitologia Africana Tribale">Africana Tribale</option>' +
'<option value="Mitologia Afroamericana">Afroamericana</option>' +
'<option value="Mitologia Artica">Artica</option>' +
'<option value="Mitologia Asiatica Tribale">Asiatica Tribale</option>' +
'<option value="Mitologia Baltica">Baltica</option>' +
'<option value="Mitologia Basca">Basca</option>' +
'<option value="Mitologia Celtica">Celtica</option>' +
'<option value="Mitologia Classica">Classica</option>' +
'<option value="Mitologia Egizia">Egizia</option>' +
'<option value="Mitologia Germanica">Germanica</option>' +
'<option value="Mitologia Indo-Iranica">Indo-Iranica</option>' +
'<option value="Mitologia Islamica">Islamica</option>' +
'<option value="Mitologia Medievale">Medievale</option>' +
'<option value="Mitologia Mesopotamica">Mesopotamica</option>' +
'<option value="Mitologia Oceanica">Oceanica</option>' +
'<option value="Mitologia Orientale">Orientale</option>' +
'<option value="Mitologia Pellerossa">Pellerossa</option>' +
'<option value="Mitologia Precolombiana">Precolombiana</option>' +
'<option value="Mitologia Semitica">Semitica</option>' +
'<option value="Mitologia Slava">Slava</option>' +
'<option value="Mitologia Ugro-Finnica">Ugro-Finnica</option>' +
'</select>' +
'<div id="fon-mit-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sottosezione</label>' +
'<select id="fon-mit-specific" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div></div>' +
'<div id="fon-folk-wrapper" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sezione</label>' +
'<select id="fon-folk-sezione" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Creature Fantastiche">Creature Fantastiche</option>' +
'<option value="Itinerari Arcani">Itinerari Arcani</option>' +
'<option value="Misteri">Misteri</option>' +
'<option value="Simboli">Simboli</option>' +
'</select></div>' +
'</div>';
// Blocco tipologia (Tipologia → Sottotipologia → Specificità)
html += '<div>' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Tipologia</label>' +
'<select id="fon-tipologia" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option>Divinità</option><option>Semidèi</option><option>Creature Fantastiche</option>' +
'<option>Umani</option><option>Luoghi</option><option>Oggetti</option>' +
'<option>Eventi</option><option>Concetti</option>' +
'</select>' +
'<div id="fon-sottotipo-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sottotipologia</label>' +
'<select id="fon-sottotipo" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div>' +
'<div id="fon-specificita-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Specificità</label>' +
'<select id="fon-specificita" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div>' +
'</div>';
html += '</div></div>';
// PANNELLO LETTERATURA
html += '<div id="ri-letteratura-panel" style="display:none">';
html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;margin-bottom:1.5rem">';
// Autore (popolato da MediaWiki:Scrittori.json)
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Autore</label>' +
'<select id="let-autore" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'</select></div>';
// Forma
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Forma</label>' +
'<select id="let-forma" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option>Biografia</option><option>Fumetto</option><option>Poesia</option>' +
'<option>Racconto</option><option>Romanzo</option>' +
'</select></div>';
// Genere
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Genere</label>' +
'<select id="let-genere" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'<option>Avventura</option><option>Fantascienza</option><option>Fantastico</option>' +
'<option>Fantasy</option><option>Horror</option>' +
'</select></div>';
// In Biblioteca
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">In Biblioteca</label>' +
'<select id="let-biblioteca" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'<option value="Biblioteca">Sì</option>' +
'</select></div>';
// Epoca / Datazione
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Epoca</label>' +
epocaSelectHtml('let-epoca') + '</div>';
// Blocco geografico (Continente → Area → Paese)
html += geoBlock('let-');
// Blocco sezioni (identico a Fonti)
html += '<div>' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Macrosezione</label>' +
'<select id="let-macrosezione" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Mitologia">Mitologia</option>' +
'<option value="Folklore">Folklore</option>' +
'</select>' +
'<div id="let-mit-wrapper" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sezione</label>' +
'<select id="let-mit-macro" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Mitologia Africana Tribale">Africana Tribale</option>' +
'<option value="Mitologia Afroamericana">Afroamericana</option>' +
'<option value="Mitologia Artica">Artica</option>' +
'<option value="Mitologia Asiatica Tribale">Asiatica Tribale</option>' +
'<option value="Mitologia Baltica">Baltica</option>' +
'<option value="Mitologia Basca">Basca</option>' +
'<option value="Mitologia Celtica">Celtica</option>' +
'<option value="Mitologia Classica">Classica</option>' +
'<option value="Mitologia Egizia">Egizia</option>' +
'<option value="Mitologia Germanica">Germanica</option>' +
'<option value="Mitologia Indo-Iranica">Indo-Iranica</option>' +
'<option value="Mitologia Islamica">Islamica</option>' +
'<option value="Mitologia Medievale">Medievale</option>' +
'<option value="Mitologia Mesopotamica">Mesopotamica</option>' +
'<option value="Mitologia Oceanica">Oceanica</option>' +
'<option value="Mitologia Orientale">Orientale</option>' +
'<option value="Mitologia Pellerossa">Pellerossa</option>' +
'<option value="Mitologia Precolombiana">Precolombiana</option>' +
'<option value="Mitologia Semitica">Semitica</option>' +
'<option value="Mitologia Slava">Slava</option>' +
'<option value="Mitologia Ugro-Finnica">Ugro-Finnica</option>' +
'</select>' +
'<div id="let-mit-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sottosezione</label>' +
'<select id="let-mit-specific" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div></div>' +
'<div id="let-folk-wrapper" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sezione</label>' +
'<select id="let-folk-sezione" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Creature Fantastiche">Creature Fantastiche</option>' +
'<option value="Itinerari Arcani">Itinerari Arcani</option>' +
'<option value="Misteri">Misteri</option>' +
'<option value="Simboli">Simboli</option>' +
'</select></div>' +
'</div>';
// Blocco tipologia (identico a Fonti)
html += '<div>' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Tipologia</label>' +
'<select id="let-tipologia" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option>Divinità</option><option>Semidèi</option><option>Creature Fantastiche</option>' +
'<option>Umani</option><option>Luoghi</option><option>Oggetti</option>' +
'<option>Eventi</option><option>Concetti</option>' +
'</select>' +
'<div id="let-sottotipo-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sottotipologia</label>' +
'<select id="let-sottotipo" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div>' +
'<div id="let-specificita-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Specificità</label>' +
'<select id="let-specificita" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div>' +
'</div>';
html += '</div></div>';
// PANNELLO MUSICA
html += '<div id="ri-musica-panel" style="display:none">';
html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;margin-bottom:1.5rem">';
// Autore (select da MediaWiki:Musicisti.json)
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Autore</label>' +
'<select id="mus-autore" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'</select></div>';
// Tipo
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Tipo</label>' +
'<select id="mus-tipo" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'<option>Canzone</option><option>Album</option><option>Colonna Sonora</option>' +
'<option>Opera</option><option>Sinfonia</option><option>Poema Sinfonico</option>' +
'</select></div>';
// Genere
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Genere</label>' +
'<select id="mus-genere" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'<option>Blues</option><option>Classical</option><option>Country</option>' +
'<option>Electronic</option><option>Folk</option><option>Jazz</option>' +
'<option>Metal</option><option>Pop</option><option>Rock</option>' +
'<option>Soul/R&B</option><option>World</option>' +
'</select></div>';
// Epoca / Anno
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Epoca</label>' +
epocaSelectHtml('mus-epoca') + '</div>';
html += geoBlock('mus-');
// Blocco sezioni (identico a Fonti e Letteratura)
html += '<div>' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Macrosezione</label>' +
'<select id="mus-macrosezione" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Mitologia">Mitologia</option>' +
'<option value="Folklore">Folklore</option>' +
'</select>' +
'<div id="mus-mit-wrapper" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sezione</label>' +
'<select id="mus-mit-macro" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Mitologia Africana Tribale">Africana Tribale</option>' +
'<option value="Mitologia Afroamericana">Afroamericana</option>' +
'<option value="Mitologia Artica">Artica</option>' +
'<option value="Mitologia Asiatica Tribale">Asiatica Tribale</option>' +
'<option value="Mitologia Baltica">Baltica</option>' +
'<option value="Mitologia Basca">Basca</option>' +
'<option value="Mitologia Celtica">Celtica</option>' +
'<option value="Mitologia Classica">Classica</option>' +
'<option value="Mitologia Egizia">Egizia</option>' +
'<option value="Mitologia Germanica">Germanica</option>' +
'<option value="Mitologia Indo-Iranica">Indo-Iranica</option>' +
'<option value="Mitologia Islamica">Islamica</option>' +
'<option value="Mitologia Medievale">Medievale</option>' +
'<option value="Mitologia Mesopotamica">Mesopotamica</option>' +
'<option value="Mitologia Oceanica">Oceanica</option>' +
'<option value="Mitologia Orientale">Orientale</option>' +
'<option value="Mitologia Pellerossa">Pellerossa</option>' +
'<option value="Mitologia Precolombiana">Precolombiana</option>' +
'<option value="Mitologia Semitica">Semitica</option>' +
'<option value="Mitologia Slava">Slava</option>' +
'<option value="Mitologia Ugro-Finnica">Ugro-Finnica</option>' +
'</select>' +
'<div id="mus-mit-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sottosezione</label>' +
'<select id="mus-mit-specific" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div></div>' +
'<div id="mus-folk-wrapper" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sezione</label>' +
'<select id="mus-folk-sezione" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Creature Fantastiche">Creature Fantastiche</option>' +
'<option value="Itinerari Arcani">Itinerari Arcani</option>' +
'<option value="Misteri">Misteri</option>' +
'<option value="Simboli">Simboli</option>' +
'</select></div>' +
'</div>';
// Blocco tipologia (identico agli altri pannelli)
html += '<div>' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Tipologia</label>' +
'<select id="mus-tipologia" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option>Divinità</option><option>Semidèi</option><option>Creature Fantastiche</option>' +
'<option>Umani</option><option>Luoghi</option><option>Oggetti</option>' +
'<option>Eventi</option><option>Concetti</option>' +
'</select>' +
'<div id="mus-sottotipo-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sottotipologia</label>' +
'<select id="mus-sottotipo" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div>' +
'<div id="mus-specificita-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Specificità</label>' +
'<select id="mus-specificita" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div>' +
'</div>';
html += '</div></div>';
// PANNELLO CINEMA
html += '<div id="ri-cinema-panel" style="display:none">';
html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;margin-bottom:1.5rem">';
// Regista (select da MediaWiki:Registi.json)
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Regista</label>' +
'<select id="cin-regista" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'</select></div>';
// Tipo
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Tipo</label>' +
'<select id="cin-tipo" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'<option>Animazione</option><option>Cortometraggio</option><option>Lungometraggio</option>' +
'<option>Documentario</option><option>Serie TV</option>' +
'</select></div>';
// Genere
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Genere</label>' +
'<select id="cin-genere" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'<option>Avventura</option><option>Commedia</option><option>Drammatico</option>' +
'<option>Epico</option><option>Fantascienza</option><option>Fantastico</option>' +
'<option>Horror</option><option>Thriller</option>' +
'</select></div>';
// Epoca / Anno
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Epoca</label>' +
epocaSelectHtml('cin-epoca', EPOCHE_CINEMA) + '</div>';
// Blocco geografico
html += geoBlock('cin-');
// Blocco sezioni
html += '<div>' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Macrosezione</label>' +
'<select id="cin-macrosezione" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Mitologia">Mitologia</option>' +
'<option value="Folklore">Folklore</option>' +
'</select>' +
'<div id="cin-mit-wrapper" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sezione</label>' +
'<select id="cin-mit-macro" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Mitologia Africana Tribale">Africana Tribale</option>' +
'<option value="Mitologia Afroamericana">Afroamericana</option>' +
'<option value="Mitologia Artica">Artica</option>' +
'<option value="Mitologia Asiatica Tribale">Asiatica Tribale</option>' +
'<option value="Mitologia Baltica">Baltica</option>' +
'<option value="Mitologia Basca">Basca</option>' +
'<option value="Mitologia Celtica">Celtica</option>' +
'<option value="Mitologia Classica">Classica</option>' +
'<option value="Mitologia Egizia">Egizia</option>' +
'<option value="Mitologia Germanica">Germanica</option>' +
'<option value="Mitologia Indo-Iranica">Indo-Iranica</option>' +
'<option value="Mitologia Islamica">Islamica</option>' +
'<option value="Mitologia Medievale">Medievale</option>' +
'<option value="Mitologia Mesopotamica">Mesopotamica</option>' +
'<option value="Mitologia Oceanica">Oceanica</option>' +
'<option value="Mitologia Orientale">Orientale</option>' +
'<option value="Mitologia Pellerossa">Pellerossa</option>' +
'<option value="Mitologia Precolombiana">Precolombiana</option>' +
'<option value="Mitologia Semitica">Semitica</option>' +
'<option value="Mitologia Slava">Slava</option>' +
'<option value="Mitologia Ugro-Finnica">Ugro-Finnica</option>' +
'</select>' +
'<div id="cin-mit-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sottosezione</label>' +
'<select id="cin-mit-specific" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div></div>' +
'<div id="cin-folk-wrapper" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sezione</label>' +
'<select id="cin-folk-sezione" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Creature Fantastiche">Creature Fantastiche</option>' +
'<option value="Itinerari Arcani">Itinerari Arcani</option>' +
'<option value="Misteri">Misteri</option>' +
'<option value="Simboli">Simboli</option>' +
'</select></div>' +
'</div>';
// Blocco tipologia
html += '<div>' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Tipologia</label>' +
'<select id="cin-tipologia" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option>Divinità</option><option>Semidèi</option><option>Creature Fantastiche</option>' +
'<option>Umani</option><option>Luoghi</option><option>Oggetti</option>' +
'<option>Eventi</option><option>Concetti</option>' +
'</select>' +
'<div id="cin-sottotipo-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sottotipologia</label>' +
'<select id="cin-sottotipo" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div>' +
'<div id="cin-specificita-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Specificità</label>' +
'<select id="cin-specificita" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div>' +
'</div>';
html += '</div></div>';
// PANNELLO MUSEO
html += '<div id="ri-museo-panel" style="display:none">';
html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;margin-bottom:1.5rem">';
// Autore (da MuseoAutori.json)
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Autore</label>' +
'<select id="mus2-autore" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'</select></div>';
// Periodo
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Periodo</label>' +
'<select id="mus2-periodo" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'<option>Arte Antica</option><option>Arte Medievale</option>' +
'<option>Arte Moderna</option><option>Arte Contemporanea</option>' +
'</select></div>';
// Tipologia
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Tipologia</label>' +
'<select id="mus2-tipologia" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option>Ceramica</option><option>Disegno</option>' +
'<option>Pittura</option><option>Scultura</option>' +
'</select></div>';
// Tecnica
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Tecnica</label>' +
'<select id="mus2-tecnica" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option>Acquarello su carta</option><option>Acquarello su tela</option><option>Acquerello su tavola</option>' +
'<option>Acrilico su pannello</option><option>Acrilico su tela</option><option>Affresco</option>' +
'<option>Altorilievo</option><option>Bassorilievo</option><option>Calcare</option>' +
'<option>Cammeo</option><option>Carboncino</option><option>Crayon</option>' +
'<option>Figure Nere</option><option>Figure Rosse</option><option>Gessetto</option>' +
'<option>Gesso</option><option>Gruppo scultoreo</option><option>Guazzo</option>' +
'<option>Inchiostro</option><option>Incisione</option><option>Intarsio</option>' +
'<option>Litografia</option><option>Maioliche</option><option>Matita e acquerelli</option>' +
'<option>Matita su carta</option><option>Mosaico</option>' +
'<option>Olio e tempera su cartone</option><option>Olio su alabastro</option>' +
'<option>Olio su carta</option><option>Olio su intonaco</option>' +
'<option>Olio su pannello</option><option>Olio su rame</option>' +
'<option>Olio su tavola</option><option>Olio su tela</option>' +
'<option>Pastelli</option><option>Penna e inchiostro</option>' +
'<option>Sanguigna e grafite su carta</option><option>Statua</option>' +
'<option>Statua in bronzo</option><option>Statua in legno</option>' +
'<option>Statua in marmo</option><option>Statua in pietra</option>' +
'<option>Statua in stucco</option><option>Statua in terracotta</option>' +
'<option>Tempera su carta</option><option>Tempera su pannello</option>' +
'<option>Tempera su tela</option><option>Vetrata artistica</option><option>Xilografia</option>' +
'</select></div>';
// Stile
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Stile</label>' +
'<select id="mus2-stile" style="width:100%;font-size:15px">' +
'<option value="">— tutti —</option>' +
'<option>Accademico</option><option>Arcaico</option><option>Art Déco</option>' +
'<option>Barocco</option><option>Bizantino</option><option>Classico</option>' +
'<option>Egizio</option><option>Ellenistico</option><option>Golden Age</option>' +
'<option>Gotico</option><option>Imperiale</option><option>Impressionista</option>' +
'<option>Liberty</option><option>Manierista</option><option>Neoclassico</option>' +
'<option>Preraffaellita</option><option>Realista</option><option>Repubblicano</option>' +
'<option>Rinascimentale</option><option>Rococò</option><option>Romantico</option>' +
'<option>Simbolista</option><option>Vittoriano</option>' +
'</select></div>';
// Ubicazione (da MuseoUbicazioni.json)
html += '<div><label style="font-size:15px;display:block;margin-bottom:4px">Ubicazione</label>' +
'<select id="mus2-ubicazione" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'</select></div>';
// Blocco geografico
html += geoBlock('mus2-');
// Blocco sezioni
html += '<div>' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Macrosezione</label>' +
'<select id="mus2-macrosezione" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Mitologia">Mitologia</option>' +
'<option value="Folklore">Folklore</option>' +
'</select>' +
'<div id="mus2-mit-wrapper" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sezione</label>' +
'<select id="mus2-mit-macro" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Mitologia Africana Tribale">Africana Tribale</option>' +
'<option value="Mitologia Afroamericana">Afroamericana</option>' +
'<option value="Mitologia Artica">Artica</option>' +
'<option value="Mitologia Asiatica Tribale">Asiatica Tribale</option>' +
'<option value="Mitologia Baltica">Baltica</option>' +
'<option value="Mitologia Basca">Basca</option>' +
'<option value="Mitologia Celtica">Celtica</option>' +
'<option value="Mitologia Classica">Classica</option>' +
'<option value="Mitologia Egizia">Egizia</option>' +
'<option value="Mitologia Germanica">Germanica</option>' +
'<option value="Mitologia Indo-Iranica">Indo-Iranica</option>' +
'<option value="Mitologia Islamica">Islamica</option>' +
'<option value="Mitologia Medievale">Medievale</option>' +
'<option value="Mitologia Mesopotamica">Mesopotamica</option>' +
'<option value="Mitologia Oceanica">Oceanica</option>' +
'<option value="Mitologia Orientale">Orientale</option>' +
'<option value="Mitologia Pellerossa">Pellerossa</option>' +
'<option value="Mitologia Precolombiana">Precolombiana</option>' +
'<option value="Mitologia Semitica">Semitica</option>' +
'<option value="Mitologia Slava">Slava</option>' +
'<option value="Mitologia Ugro-Finnica">Ugro-Finnica</option>' +
'</select>' +
'<div id="mus2-mit-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sottosezione</label>' +
'<select id="mus2-mit-specific" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div></div>' +
'<div id="mus2-folk-wrapper" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sezione</label>' +
'<select id="mus2-folk-sezione" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option value="Creature Fantastiche">Creature Fantastiche</option>' +
'<option value="Itinerari Arcani">Itinerari Arcani</option>' +
'<option value="Misteri">Misteri</option>' +
'<option value="Simboli">Simboli</option>' +
'</select></div>' +
'</div>';
// Blocco tipologia (Voci)
html += '<div>' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Tipologia Mitologica</label>' +
'<select id="mus2-tipologia-mit" style="width:100%;font-size:15px">' +
'<option value="">— tutte —</option>' +
'<option>Divinità</option><option>Semidèi</option><option>Creature Fantastiche</option>' +
'<option>Umani</option><option>Luoghi</option><option>Oggetti</option>' +
'<option>Eventi</option><option>Concetti</option>' +
'</select>' +
'<div id="mus2-sottotipo-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Sottotipologia</label>' +
'<select id="mus2-sottotipo" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div>' +
'<div id="mus2-specificita-sub" style="display:none;margin-top:6px">' +
'<label style="font-size:15px;display:block;margin-bottom:4px">Specificità</label>' +
'<select id="mus2-specificita" style="width:100%;font-size:15px"><option value="">— tutte —</option></select>' +
'</div>' +
'</div>';
html += '</div></div>';
// BOTTONI E RISULTATI
html += '<div style="display:flex;gap:8px;margin-bottom:1.5rem;flex-wrap:wrap"><button id="ri-btn-cerca">Cerca</button><button id="ri-btn-reset">Azzera filtri</button></div>';
html += '<div id="ri-loading" style="display:none;font-size:15px;color:var(--color-text-secondary)">Ricerca in corso...</div>';
html += '<div id="ri-loading-cats" style="display:none;font-size:15px;color:var(--color-text-secondary)">Caricamento dati voci...</div>';
html += '<div id="ri-empty" style="display:none;font-size:15px;color:var(--color-text-secondary)"></div>';
html += '<div id="ri-results" style="display:none">' +
'<p id="ri-count" style="font-size:15px;color:var(--color-text-secondary);margin-bottom:8px"></p>' +
'<div style="overflow-x:auto"><table id="ri-table" style="width:100%;border-collapse:collapse;font-size:15px">' +
'<thead><tr id="ri-thead-row">' +
'<th style="text-align:left;padding:6px 8px;border-bottom:1px solid var(--color-border-tertiary);cursor:pointer" data-col="0">Voce ↕</th>' +
'<th style="text-align:left;padding:6px 8px;border-bottom:1px solid var(--color-border-tertiary);cursor:pointer" data-col="1">Tipologia ↕</th>' +
'<th style="text-align:left;padding:6px 8px;border-bottom:1px solid var(--color-border-tertiary);cursor:pointer" data-col="2">Sesso ↕</th>' +
'<th style="text-align:left;padding:6px 8px;border-bottom:1px solid var(--color-border-tertiary);cursor:pointer" data-col="3">Aspetto ↕</th>' +
'<th style="text-align:left;padding:6px 8px;border-bottom:1px solid var(--color-border-tertiary);cursor:pointer" data-col="4">Indole ↕</th>' +
'<th style="text-align:left;padding:6px 8px;border-bottom:1px solid var(--color-border-tertiary);cursor:pointer" data-col="5">Elemento ↕</th>' +
'<th style="text-align:left;padding:6px 8px;border-bottom:1px solid var(--color-border-tertiary);cursor:pointer" data-col="6">Habitat ↕</th>' +
'</tr></thead><tbody id="ri-tbody"></tbody></table></div>' +
'<button id="ri-load-more" style="display:none;margin-top:1rem">Carica altri</button></div>';
html += '</div>';
document.getElementById('ricerca-incrociata').innerHTML = html;
// =====================
// STATO GLOBALE
// =====================
var allResults = [];
var loadedCount = 0;
var sortDir = {};
var currentNs = '0';
var currentArea = 'voci';
// =====================
// FUNZIONI CORE
// =====================
function getCategoryMembers(cat, ns) {
var deferred = $.Deferred();
var pages = [];
function fetch(cont) {
var params = {
action: 'query', list: 'categorymembers',
cmtitle: 'Categoria:' + cat,
cmlimit: '500',
cmnamespace: ns || '0',
format: 'json'
};
if (cont) params.cmcontinue = cont;
$.getJSON(API, params, function(d) {
(d.query && d.query.categorymembers || []).forEach(function(p) { pages.push(p.title); });
if (d.continue && d.continue.cmcontinue) { fetch(d.continue.cmcontinue); }
else { deferred.resolve(pages); }
}).fail(function() { deferred.resolve(pages); });
}
fetch(null);
return deferred.promise();
}
function getPageCategories(titles) {
var deferred = $.Deferred();
var result = {};
titles.forEach(function(t) { result[t] = []; });
var chunks = [];
for (var i = 0; i < titles.length; i += 10) { chunks.push(titles.slice(i, i + 10)); }
var done = 0;
chunks.forEach(function(chunk) {
function fetchChunk(clcontinue) {
var params = { action: 'query', prop: 'categories', titles: chunk.join('|'), cllimit: '500', format: 'json' };
if (clcontinue) params.clcontinue = clcontinue;
$.getJSON(API, params, function(d) {
var pages = d.query && d.query.pages || {};
Object.keys(pages).forEach(function(id) {
var page = pages[id];
var cats = (page.categories || []).map(function(c) { return c.title.replace('Categoria:', ''); });
result[page.title] = (result[page.title] || []).concat(cats);
});
if (d.continue && d.continue.clcontinue) { fetchChunk(d.continue.clcontinue); }
else { done++; if (done === chunks.length) deferred.resolve(result); }
}).fail(function() { done++; if (done === chunks.length) deferred.resolve(result); });
}
fetchChunk(null);
});
if (chunks.length === 0) deferred.resolve(result);
return deferred.promise();
}
// Recupera thumbnail URL per pagine File: (namespace 6)
function getImageThumbs(titles, width) {
var deferred = $.Deferred();
var result = {};
if (!titles.length) { deferred.resolve(result); return deferred.promise(); }
var chunks = [];
for (var i = 0; i < titles.length; i += 10) { chunks.push(titles.slice(i, i + 10)); }
var done = 0;
chunks.forEach(function(chunk) {
$.getJSON(API, {
action: 'query', prop: 'imageinfo', titles: chunk.join('|'),
iiprop: 'url', iiurlwidth: width || 60, format: 'json'
}, function(d) {
var pages = d.query && d.query.pages || {};
Object.keys(pages).forEach(function(id) {
var page = pages[id];
var info = page.imageinfo && page.imageinfo[0];
if (info) result[page.title] = info.thumburl || info.url || '';
});
done++; if (done === chunks.length) deferred.resolve(result);
}).fail(function() { done++; if (done === chunks.length) deferred.resolve(result); });
});
return deferred.promise();
}
function extractFromCats(cats, prefix) {
var found = [];
cats.forEach(function(c) { if (c.indexOf(prefix) === 0) found.push(c.replace(prefix, '')); });
return found.join(', ') || '—';
}
function extractSimple(cats, values) {
var found = [];
values.forEach(function(v) { if (cats.indexOf(v) !== -1) found.push(v); });
return found.join(', ') || '—';
}
function addRows(titles, catData) {
titles.forEach(function(title) {
var t = String(title);
var cats = catData[t] || [];
var tipologia = extractSimple(cats, ['Divinità','Semidei','Creature Fantastiche','Umani','Luoghi','Oggetti','Eventi','Concetti']);
var sesso = extractFromCats(cats, 'Sesso: ');
var aspetto = extractFromCats(cats, 'Aspetto: ');
var indole = extractFromCats(cats, 'Indole: ');
var elemento = extractFromCats(cats, 'Elemento: ');
var habitat = extractFromCats(cats, 'Habitat: ');
$('#ri-tbody').append('<tr>' +
tdLink(t, t) +
td(tipologia) + td(sesso) + td(aspetto) + td(indole) + td(elemento) + td(habitat) +
'</tr>');
});
}
// Colonne dinamiche per area tematica
var TH_STYLE = 'text-align:left;padding:6px 8px;border-bottom:1px solid var(--color-border-tertiary);cursor:pointer';
var COLS_VOCI = ['Voce','Tipologia','Sesso','Aspetto','Indole','Elemento','Habitat'];
var COLS_FONTI = ['Fonte','Tipo','Forma','Biblioteca','Sezione','Paese'];
var COLS_LETTERATURA = ['Opera','Autore','Forma','Genere','Biblioteca','Sezione','Paese'];
var COLS_MUSICA = ['Brano/Album','Autore','Tipo','Genere','Sezione','Paese'];
var COLS_CINEMA = ['Film','Regista','Tipo','Genere','Sezione','Paese'];
var COLS_RACCONTI = ['Racconto','Autore','Tipologia','Paese','Sezione'];
var COLS_MUSEO = ['Opera','Autore','Periodo','Datazione','Stile','Tipologia','Immagine'];
function setTableColumns(cols) {
var $tr = $('#ri-thead-row').empty();
cols.forEach(function(c, i) {
$tr.append('<th style="' + TH_STYLE + '" data-col="' + i + '">' + c + ' ↕</th>');
});
sortDir = {};
}
// =====================
// HELPER RENDERING RIGHE
// =====================
var TD = 'padding:5px 8px;border-bottom:0.5px solid var(--color-border-tertiary)';
function td(val) {
return '<td style="' + TD + '">' + (val || '—') + '</td>';
}
function tdLink(fullTitle, displayTitle) {
return '<td style="' + TD + '"><a href="/index.php?title=' +
encodeURIComponent(fullTitle.replace(/ /g, '_')) + '">' + displayTitle + '</a></td>';
}
function estraiSezione(cats) {
var val = '—';
Object.keys(SUBMITS).forEach(function(macro) {
SUBMITS[macro].forEach(function(sub) {
if (cats.indexOf(sub) !== -1 && val === '—') val = sub.replace('Mitologia ', '');
});
});
if (val === '—') SEZIONI_MIT.forEach(function(s) {
if (cats.indexOf(s) !== -1 && val === '—') val = s.replace('Mitologia ', '');
});
if (val === '—') SEZIONI_FOLK.forEach(function(s) {
if (cats.indexOf(s) !== -1 && val === '—') val = s;
});
if (val === '—') val = extractSimple(cats, ['Mitologia','Folklore']);
return val;
}
function estraiPaese(cats) {
var val = '—';
GEO_PAESI.forEach(function(p) { if (cats.indexOf(p) !== -1 && val === '—') val = p; });
if (val === '—') GEO_AREE.forEach(function(a) { if (cats.indexOf(a) !== -1 && val === '—') val = a; });
if (val === '—') GEO_CONTINENTI.forEach(function(c) { if (cats.indexOf(c) !== -1 && val === '—') val = c; });
return val;
}
// Trova il valore di un campo aperto (autore/regista) per intersezione con il JSON
function trovaNelJson(cats, jsonArray) {
if (!jsonArray) return '—';
for (var i = 0; i < cats.length; i++) {
if (jsonArray.indexOf(cats[i]) !== -1) return cats[i];
}
return '—';
}
// =====================
// ADDROWS PER AREA
// =====================
// addRows per le Fonti
var FORME_FONTI = ['Annali/Opere Storiche','Canti/Inni/Poesie','Codici/Libri Illustrati','Dizionari/Enciclopedie','Mitografie','Opere Epiche','Opere Naturalistiche','Peripli/Opere Geografiche','Saggi','Testi Sacri','Tragedie'];
var SEZIONI_MIT = Object.keys(SUBMITS);
var SEZIONI_FOLK = ['Creature Fantastiche','Itinerari Arcani','Misteri','Simboli'];
// Liste geo condivise
var GEO_CONTINENTI = ['Europa','Africa','Asia','Americhe','Oceania','Poli'];
var GEO_AREE = [];
var GEO_PAESI = [];
function buildGeoLists() {
GEO_AREE = [];
GEO_PAESI = [];
Object.keys(AREE).forEach(function(cont) { AREE[cont].forEach(function(a) { GEO_AREE.push(a); }); });
Object.keys(GEO).forEach(function(cont) {
Object.keys(GEO[cont]).forEach(function(area) {
Object.keys(GEO[cont][area]).forEach(function(p) { GEO_PAESI.push(p); });
});
});
}
function addRowsFonti(titles, catData) {
buildGeoLists();
titles.forEach(function(title) {
var t = String(title);
var cats = catData[t] || [];
var tipo = extractSimple(cats, ['Fonti Antiche','Fonti Moderne']);
var forma = extractSimple(cats, FORME_FONTI);
var biblioteca = cats.indexOf('Biblioteca') !== -1 ? 'Sì' : '—';
var sezioneVal = estraiSezione(cats);
var paeseVal = estraiPaese(cats);
var display = t.replace(/^Fonti:/, '');
$('#ri-tbody').append('<tr>' +
tdLink(t, display) + td(tipo) + td(forma) + td(biblioteca) + td(sezioneVal) + td(paeseVal) +
'</tr>');
});
}
var FORME_LETT = ['Biografia','Fumetto','Poesia','Racconto','Romanzo'];
var GENERI_LETT = ['Avventura','Fantascienza','Fantastico','Fantasy','Horror'];
function addRowsLetteratura(titles, catData) {
buildGeoLists();
titles.forEach(function(title) {
var t = String(title);
var cats = catData[t] || [];
var autore = trovaNelJson(cats, scrittoriData);
var forma = extractSimple(cats, FORME_LETT);
var genere = extractSimple(cats, GENERI_LETT);
var biblioteca = cats.indexOf('Biblioteca') !== -1 ? 'Sì' : '—';
var sezioneVal = estraiSezione(cats);
var paeseVal = estraiPaese(cats);
var display = t.replace(/^Letteratura:/, '');
$('#ri-tbody').append('<tr>' +
tdLink(t, display) + td(autore) + td(forma) + td(genere) + td(biblioteca) + td(sezioneVal) + td(paeseVal) +
'</tr>');
});
}
var TIPI_MUSICA = ['Canzone','Album','Colonna Sonora','Opera','Sinfonia','Poema Sinfonico'];
var GENERI_MUSICA = ['Blues','Classical','Country','Electronic','Folk','Jazz','Metal','Pop','Rock','Soul/R&B','World'];
function addRowsMusica(titles, catData) {
buildGeoLists();
titles.forEach(function(title) {
var t = String(title);
var cats = catData[t] || [];
var autore = trovaNelJson(cats, musicistiData);
var tipo = extractSimple(cats, TIPI_MUSICA);
var genere = extractSimple(cats, GENERI_MUSICA);
var sezioneVal = estraiSezione(cats);
var paeseVal = estraiPaese(cats);
var display = t.replace(/^Musica:/, '');
$('#ri-tbody').append('<tr>' +
tdLink(t, display) + td(autore) + td(tipo) + td(genere) + td(sezioneVal) + td(paeseVal) +
'</tr>');
});
}
var TIPI_CINEMA = ['Animazione','Cortometraggio','Lungometraggio','Documentario','Serie TV'];
var GENERI_CINEMA = ['Avventura','Commedia','Drammatico','Epico','Fantascienza','Fantastico','Horror','Thriller'];
function addRowsCinema(titles, catData) {
buildGeoLists();
titles.forEach(function(title) {
var t = String(title);
var cats = catData[t] || [];
var regista = trovaNelJson(cats, registiData);
var tipo = extractSimple(cats, TIPI_CINEMA);
var genere = extractSimple(cats, GENERI_CINEMA);
var sezioneVal = estraiSezione(cats);
var paeseVal = estraiPaese(cats);
var display = t.replace(/^Cinema:/, '');
$('#ri-tbody').append('<tr>' +
tdLink(t, display) + td(regista) + td(tipo) + td(genere) + td(sezioneVal) + td(paeseVal) +
'</tr>');
});
}
var TIPOLOGIE_RACCONTO = ['Favole','Fiabe','Leggende','Miti','Racconti Popolari'];
var SEZIONI_RACCONTO = ['Itinerari Arcani','Misteri','Simboli','Altre Mitologie'];
function addRowsRacconti(titles, catData) {
buildGeoLists();
titles.forEach(function(title) {
var t = String(title);
var cats = catData[t] || [];
var autore = trovaNelJson(cats, scrittoriData);
var tipologia = extractSimple(cats, TIPOLOGIE_RACCONTO);
var sezione = estraiSezione(cats);
if (sezione === '—') sezione = extractSimple(cats, SEZIONI_RACCONTO);
var paese = estraiPaese(cats);
var display = t.replace(/^Racconti:/, '');
$('#ri-tbody').append('<tr>' +
tdLink(t, display) + td(autore) + td(tipologia) + td(paese) + td(sezione) +
'</tr>');
});
}
var EPOCA_PREFIX = { fonti: 'fon', letteratura: 'let', musica: 'mus', cinema: 'cin' };
function getEpocaId() {
var p = EPOCA_PREFIX[currentArea];
return p ? $('#' + p + '-epoca').val() : '0';
}
function filterByEpoca(batch, catData) {
var epIdx = getEpocaId();
if (!epIdx || epIdx === '0') return batch;
var epoche = (currentArea === 'cinema') ? EPOCHE_CINEMA : EPOCHE;
return batch.filter(function(t) {
return annoInEpoca(catData[String(t)] || [], epIdx, epoche);
});
}
var TIPOLOGIE_MUSEO = ['Ceramica','Disegno','Pittura','Scultura'];
var PERIODI_MUSEO = ['Arte Antica','Arte Medievale','Arte Moderna','Arte Contemporanea'];
var STILI_MUSEO = ['Accademico','Arcaico','Art Déco','Barocco','Bizantino','Classico',
'Egizio','Ellenistico','Golden Age','Gotico','Imperiale','Impressionista',
'Liberty','Manierista','Neoclassico','Preraffaellita','Preraffaelliti',
'Realista','Repubblicano','Rinascimentale','Rococò','Romantico','Simbolista','Vittoriano'];
var TECNICHE_MUSEO = ['Acquarello su carta','Acquarello su tela','Acquerello su tavola',
'Acrilico su pannello','Acrilico su tela','Affresco','Altorilievo','Bassorilievo',
'Calcare','Cammeo','Carboncino','Crayon','Figure Nere','Figure Rosse','Gessetto',
'Gesso','Gruppo scultoreo','Guazzo','Inchiostro','Incisione','Intarsio','Litografia',
'Maioliche','Matita e acquerelli','Matita su carta','Mosaico',
'Olio e tempera su cartone','Olio su alabastro','Olio su carta','Olio su intonaco',
'Olio su pannello','Olio su rame','Olio su tavola','Olio su tela','Pastelli',
'Penna e inchiostro','Sanguigna e grafite su carta','Statua','Statua in bronzo',
'Statua in legno','Statua in marmo','Statua in pietra','Statua in stucco',
'Statua in terracotta','Tempera su carta','Tempera su pannello','Tempera su tela',
'Vetrata artistica','Xilografia'];
function addRowsMuseo(titles, catData, thumbData) {
buildGeoLists();
var thumbs = thumbData || {};
titles.forEach(function(title) {
var t = String(title);
var cats = catData[t] || [];
var autore = trovaNelJson(cats, museoAutoriData);
var periodo = extractSimple(cats, PERIODI_MUSEO);
var stile = extractSimple(cats, STILI_MUSEO);
var tipologia = extractSimple(cats, TIPOLOGIE_MUSEO);
var datazione = '—';
cats.forEach(function(c) {
if (c.indexOf('Anno: ') === 0) datazione = c.replace('Anno: ', '');
});
var thumbUrl = thumbs[t] || '';
var thumbHtml = thumbUrl
? '<a href="/index.php?title=' + encodeURIComponent(t.replace(/ /g, '_')) + '">' +
'<img src="' + thumbUrl + '" style="height:50px;width:auto;display:block;border-radius:2px"></a>'
: '—';
var display = t.replace(/^File:/, '').replace(/\.[^.]+$/, ''); // rimuove File: e estensione
$('#ri-tbody').append('<tr>' +
tdLink(t, display) +
td(autore) + td(periodo) + td(datazione) + td(stile) + td(tipologia) +
'<td style="' + TD + '">' + thumbHtml + '</td>' +
'</tr>');
});
}
function loadBatch() {
var batch = allResults.slice(loadedCount, loadedCount + LIMIT);
if (batch.length === 0) return;
$('#ri-loading-cats').show();
getPageCategories(batch).done(function(catData) {
$('#ri-loading-cats').hide();
var filtered = filterByEpoca(batch, catData);
if (currentArea === 'museo') {
getImageThumbs(filtered, 60).done(function(thumbData) {
addRowsMuseo(filtered, catData, thumbData);
});
} else if (currentArea === 'fonti') {
addRowsFonti(filtered, catData);
} else if (currentArea === 'letteratura') {
addRowsLetteratura(filtered, catData);
} else if (currentArea === 'musica') {
addRowsMusica(filtered, catData);
} else if (currentArea === 'cinema') {
addRowsCinema(filtered, catData);
} else if (currentArea === 'racconti') {
addRowsRacconti(filtered, catData);
} else {
addRows(filtered, catData);
}
loadedCount += batch.length;
if (loadedCount < allResults.length) { $('#ri-load-more').show(); } else { $('#ri-load-more').hide(); }
});
}
function intersect(sets) {
if (sets.length === 0) return [];
var result = new Set(sets[0]);
for (var i = 1; i < sets.length; i++) {
var s = new Set(sets[i]);
result.forEach(function(x) { if (!s.has(x)) result.delete(x); });
}
return [...result].sort(function(a, b) { return a.localeCompare(b, 'it'); });
}
function runSearch(cats, ns) {
if (cats.length === 0) { $('#ri-empty').text('Seleziona almeno un filtro.').show(); return; }
$('#ri-loading').show();
$('#ri-results').hide();
$('#ri-empty').hide();
$('#ri-tbody').empty();
var promises = cats.map(function(c) { return getCategoryMembers(c, ns); });
$.when.apply($, promises).done(function() {
var args = Array.prototype.slice.call(arguments);
var sets = promises.length === 1 ? [args[0]] : args.map(function(a) { return Array.isArray(a) ? a : a[0]; });
var result = intersect(sets);
$('#ri-loading').hide();
if (result.length === 0) {
$('#ri-empty').text('Nessuna voce trovata con questi filtri.').show();
} else {
allResults = result;
loadedCount = 0;
$('#ri-count').text(result.length + ' voci trovate (mostro le prime ' + Math.min(LIMIT, result.length) + ')');
$('#ri-results').show();
loadBatch();
}
});
}
// =====================
// CARICAMENTO JSON MEDIAWIKI
// =====================
function loadMediaWikiJson(title, callback) {
$.getJSON(API, {
action: 'query', prop: 'revisions', titles: title,
rvprop: 'content', rvslots: 'main', format: 'json'
}, function(d) {
try {
var pages = d.query.pages;
var content = pages[Object.keys(pages)[0]].revisions[0].slots.main['*'];
callback(JSON.parse(content));
} catch(e) { console.error('Errore caricamento ' + title, e); callback(null); }
}).fail(function() { callback(null); });
}
// Carica geo.json all'avvio
loadMediaWikiJson(API_GEO, function(json) {
geoData = json || {};
});
// =====================
// ATU
// =====================
function populateAtuMacro() {
if (!atuData) return;
var sel = $('#rac-atu-macro');
sel.html('<option value="">— tutti —</option>');
Object.keys(atuData).forEach(function(macro) {
sel.append('<option value="' + macro + '">' + macro + '</option>');
});
}
function populateAtuGruppo(macro) {
var sel = $('#rac-atu-gruppo');
sel.html('<option value="">— tutti —</option>');
$('#rac-atu-numero').html('<option value="">— tutti —</option>');
$('#rac-atu-numero-sub').hide();
if (!macro || !atuData || !atuData[macro]) { $('#rac-atu-gruppo-sub').hide(); return; }
Object.keys(atuData[macro]).forEach(function(g) {
sel.append('<option value="' + g + '">' + g + '</option>');
});
$('#rac-atu-gruppo-sub').show();
}
function populateAtuNumero(macro, gruppo) {
var sel = $('#rac-atu-numero');
sel.html('<option value="">— tutti —</option>');
if (!macro || !gruppo || !atuData || !atuData[macro] || !atuData[macro][gruppo]) {
$('#rac-atu-numero-sub').hide(); return;
}
atuData[macro][gruppo].forEach(function(v) {
sel.append('<option value="' + v.numero + '">' + v.numero + ' - ' + v.titolo + '</option>');
});
$('#rac-atu-numero-sub').show();
}
function populateAtMotivi() {
if (!atData) return;
var sel = $('#rac-at-motivi');
sel.html('');
atData.forEach(function(v) { sel.append('<option value="' + v + '">' + v + '</option>'); });
}
function populateScrittori(selId) {
if (!scrittoriData) return;
var sel = $('#' + selId);
sel.html('<option value="">— tutti —</option>');
scrittoriData.forEach(function(a) { sel.append('<option value="' + a + '">' + a + '</option>'); });
}
function populateMusicisti(selId) {
if (!musicistiData) return;
var sel = $('#' + selId);
sel.html('<option value="">— tutti —</option>');
musicistiData.forEach(function(a) { sel.append('<option value="' + a + '">' + a + '</option>'); });
}
function populateRegisti(selId) {
if (!registiData) return;
var sel = $('#' + selId);
sel.html('<option value="">— tutti —</option>');
registiData.forEach(function(a) { sel.append('<option value="' + a + '">' + a + '</option>'); });
}
function populateMuseoAutori(selId) {
if (!museoAutoriData) return;
var sel = $('#' + selId);
sel.html('<option value="">— tutti —</option>');
museoAutoriData.forEach(function(a) { sel.append('<option value="' + a + '">' + a + '</option>'); });
}
function populateMuseoUbic(selId) {
if (!museoUbicData) return;
var sel = $('#' + selId);
sel.html('<option value="">— tutte —</option>');
museoUbicData.forEach(function(u) { sel.append('<option value="' + u + '">' + u + '</option>'); });
}
function bindGeo(p) {
// Continente → Area (hardcoded)
$('#' + p + 'continente').on('change', function() {
var val = $(this).val();
$('#' + p + 'area,#' + p + 'paese,#' + p + 'regione,#' + p + 'provincia,#' + p + 'citta').html('<option value="">— tutti —</option>');
$('#' + p + 'area-sub,#' + p + 'paese-sub,#' + p + 'regione-sub,#' + p + 'provincia-sub,#' + p + 'citta-sub').hide();
if (val && AREE[val]) {
AREE[val].forEach(function(a) { $('#' + p + 'area').append('<option value="' + a + '">' + a + '</option>'); });
$('#' + p + 'area-sub').show();
}
});
// Area → Paese (hardcoded)
$('#' + p + 'area').on('change', function() {
var cont = $('#' + p + 'continente').val(), area = $(this).val();
$('#' + p + 'paese,#' + p + 'regione,#' + p + 'provincia,#' + p + 'citta').html('<option value="">— tutti —</option>');
$('#' + p + 'paese-sub,#' + p + 'regione-sub,#' + p + 'provincia-sub,#' + p + 'citta-sub').hide();
if (cont && area && GEO[cont] && GEO[cont][area]) {
Object.keys(GEO[cont][area]).forEach(function(pp) {
$('#' + p + 'paese').append('<option value="' + pp + '">' + pp + '</option>');
});
$('#' + p + 'paese-sub').show();
}
});
// Paese → Regione (dal JSON)
$('#' + p + 'paese').on('change', function() {
var paese = $(this).val();
$('#' + p + 'regione,#' + p + 'provincia,#' + p + 'citta').html('<option value="">— tutti —</option>');
$('#' + p + 'regione-sub,#' + p + 'provincia-sub,#' + p + 'citta-sub').hide();
if (paese && geoData && geoData[paese]) {
Object.keys(geoData[paese]).forEach(function(r) {
$('#' + p + 'regione').append('<option value="' + r + '">' + r + '</option>');
});
$('#' + p + 'regione-sub').show();
}
});
// Regione → Provincia (dal JSON)
$('#' + p + 'regione').on('change', function() {
var paese = $('#' + p + 'paese').val(), reg = $(this).val();
$('#' + p + 'provincia,#' + p + 'citta').html('<option value="">— tutti —</option>');
$('#' + p + 'provincia-sub,#' + p + 'citta-sub').hide();
if (paese && reg && geoData && geoData[paese] && geoData[paese][reg]) {
Object.keys(geoData[paese][reg]).forEach(function(prov) {
$('#' + p + 'provincia').append('<option value="' + prov + '">' + prov + '</option>');
});
$('#' + p + 'provincia-sub').show();
}
});
// Provincia → Città (dal JSON)
$('#' + p + 'provincia').on('change', function() {
var paese = $('#' + p + 'paese').val(),
reg = $('#' + p + 'regione').val(),
prov = $(this).val();
$('#' + p + 'citta').html('<option value="">— tutte —</option>');
$('#' + p + 'citta-sub').hide();
if (paese && reg && prov && geoData && geoData[paese] && geoData[paese][reg] && geoData[paese][reg][prov]) {
geoData[paese][reg][prov].forEach(function(c) {
$('#' + p + 'citta').append('<option value="' + c + '">' + c + '</option>');
});
$('#' + p + 'citta-sub').show();
}
});
}
// =====================
// LISTENERS
// =====================
$('#ri-table thead th').on('click', function() {
var col = parseInt($(this).data('col'));
sortDir[col] = !sortDir[col];
var rows = $('#ri-tbody tr').toArray();
rows.sort(function(a, b) {
var va = $(a).find('td').eq(col).text();
var vb = $(b).find('td').eq(col).text();
return sortDir[col] ? va.localeCompare(vb, 'it') : vb.localeCompare(va, 'it');
});
$('#ri-tbody').empty().append(rows);
});
$('#ri-load-more').on('click', function() { $(this).hide(); loadBatch(); });
$('#ri-area-tematica').on('change', function() {
var val = $(this).val();
currentNs = NS[val] || '0';
currentArea = val || 'voci';
$('#ri-voci-panel,#ri-racconti-panel,#ri-fonti-panel,#ri-letteratura-panel,#ri-musica-panel,#ri-cinema-panel,#ri-museo-panel').hide();
$('#ri-sezione-wrapper,#ri-tipo-wrapper,#ri-continente-wrapper').hide();
$('#ri-results,#ri-empty').hide();
$('#ri-tbody').empty();
allResults = [];
loadedCount = 0;
if (val === 'voci') {
$('#ri-voci-panel,#ri-sezione-wrapper,#ri-tipo-wrapper,#ri-continente-wrapper').show();
setTableColumns(COLS_VOCI);
} else if (val === 'racconti') {
$('#ri-racconti-panel').show();
setTableColumns(COLS_RACCONTI);
if (!atuData) {
loadMediaWikiJson(API_ATU, function(json) { atuData = json; populateAtuMacro(); });
} else { populateAtuMacro(); }
if (!atData) {
loadMediaWikiJson(API_AT, function(json) { atData = json; populateAtMotivi(); });
} else { populateAtMotivi(); }
} else if (val === 'fonti') {
$('#ri-fonti-panel').show();
setTableColumns(COLS_FONTI);
if (!scrittoriData) {
loadMediaWikiJson(API_SCRITTORI, function(json) {
scrittoriData = (json && json.scrittori) ? json.scrittori : (Array.isArray(json) ? json : []);
populateScrittori('fon-autore');
});
} else { populateScrittori('fon-autore'); }
} else if (val === 'letteratura') {
$('#ri-letteratura-panel').show();
setTableColumns(COLS_LETTERATURA);
if (!scrittoriData) {
loadMediaWikiJson(API_SCRITTORI, function(json) {
scrittoriData = (json && json.scrittori) ? json.scrittori : (Array.isArray(json) ? json : []);
populateScrittori('let-autore');
});
} else { populateScrittori('let-autore'); }
} else if (val === 'musica') {
$('#ri-musica-panel').show();
setTableColumns(COLS_MUSICA);
if (!musicistiData) {
loadMediaWikiJson(API_MUSICISTI, function(json) {
musicistiData = (json && json.musicisti) ? json.musicisti : (Array.isArray(json) ? json : []);
populateMusicisti('mus-autore');
});
} else { populateMusicisti('mus-autore'); }
} else if (val === 'cinema') {
$('#ri-cinema-panel').show();
setTableColumns(COLS_CINEMA);
if (!registiData) {
loadMediaWikiJson(API_REGISTI, function(json) {
registiData = (json && json.registi) ? json.registi : (Array.isArray(json) ? json : []);
populateRegisti('cin-regista');
});
} else { populateRegisti('cin-regista'); }
} else if (val === 'museo') {
$('#ri-museo-panel').show();
setTableColumns(COLS_MUSEO);
var caricaMuseo = function() {};
if (!museoAutoriData) {
loadMediaWikiJson(API_MUS_AUTORI, function(json) {
museoAutoriData = (json && json.autori) ? json.autori : (Array.isArray(json) ? json : []);
populateMuseoAutori('mus2-autore');
});
} else { populateMuseoAutori('mus2-autore'); }
if (!museoUbicData) {
loadMediaWikiJson(API_MUS_UBIC, function(json) {
museoUbicData = (json && json.ubicazioni) ? json.ubicazioni : (Array.isArray(json) ? json : []);
populateMuseoUbic('mus2-ubicazione');
});
} else { populateMuseoUbic('mus2-ubicazione'); }
}
});
$('#ri-macrosezione').on('change', function() {
var val = $(this).val();
if (val === 'Mitologia & Folklore') {
$('#ri-mit-wrapper,#ri-folk-wrapper').hide();
$('#ri-mit-macro').val('');
$('#ri-mit-specific').html('<option value="">— tutte —</option>');
$('#ri-mit-sub').hide();
$('#ri-folk-sezione').val('');
} else {
$('#ri-mit-wrapper').toggle(val === 'Mitologia');
$('#ri-folk-wrapper').toggle(val === 'Folklore');
if (val !== 'Mitologia') {
$('#ri-mit-macro').val('');
$('#ri-mit-specific').html('<option value="">— tutte —</option>');
$('#ri-mit-sub').hide();
}
if (val !== 'Folklore') { $('#ri-folk-sezione').val(''); }
}
});
$('#ri-mit-macro').on('change', function() {
var val = $(this).val();
var sub = $('#ri-mit-specific');
sub.html('<option value="">— tutte —</option>');
if (val && SUBMITS[val] && SUBMITS[val].length > 0) {
SUBMITS[val].forEach(function(s) { sub.append('<option value="' + s + '">' + s.replace('Mitologia ', '') + '</option>'); });
$('#ri-mit-sub').show();
} else { $('#ri-mit-sub').hide(); }
});
$('#ri-tipo').on('change', function() {
var val = $(this).val();
$('#ri-sottotipo').html('<option value="">— tutte —</option>');
$('#ri-specificita').html('<option value="">— tutte —</option>');
$('#ri-sottotipo-sub,#ri-specificita-sub').hide();
if (val && SOTTOTIPO[val]) {
SOTTOTIPO[val].forEach(function(s) { $('#ri-sottotipo').append('<option value="' + s + '">' + s + '</option>'); });
$('#ri-sottotipo-sub').show();
}
});
$('#ri-sottotipo').on('change', function() {
var val = $(this).val();
$('#ri-specificita').html('<option value="">— tutte —</option>');
$('#ri-specificita-sub').hide();
if (val && SPECIFICITA[val]) {
SPECIFICITA[val].forEach(function(s) { $('#ri-specificita').append('<option value="' + s + '">' + s + '</option>'); });
$('#ri-specificita-sub').show();
}
});
$('#rac-atu-macro').on('change', function() { populateAtuGruppo($(this).val()); });
$('#rac-atu-gruppo').on('change', function() { populateAtuNumero($('#rac-atu-macro').val(), $(this).val()); });
bindGeo('ri-');
bindGeo('rac-');
bindGeo('fon-');
// =====================
// LISTENERS FONTI
// =====================
$('#fon-macrosezione').on('change', function() {
var val = $(this).val();
$('#fon-mit-wrapper').toggle(val === 'Mitologia');
$('#fon-folk-wrapper').toggle(val === 'Folklore');
if (val !== 'Mitologia') {
$('#fon-mit-macro').val('');
$('#fon-mit-specific').html('<option value="">— tutte —</option>');
$('#fon-mit-sub').hide();
}
if (val !== 'Folklore') { $('#fon-folk-sezione').val(''); }
});
$('#fon-mit-macro').on('change', function() {
var val = $(this).val();
var sub = $('#fon-mit-specific');
sub.html('<option value="">— tutte —</option>');
if (val && SUBMITS[val] && SUBMITS[val].length > 0) {
SUBMITS[val].forEach(function(s) { sub.append('<option value="' + s + '">' + s.replace('Mitologia ', '') + '</option>'); });
$('#fon-mit-sub').show();
} else { $('#fon-mit-sub').hide(); }
});
$('#fon-tipologia').on('change', function() {
var val = $(this).val();
$('#fon-sottotipo').html('<option value="">— tutte —</option>');
$('#fon-specificita').html('<option value="">— tutte —</option>');
$('#fon-sottotipo-sub,#fon-specificita-sub').hide();
if (val && SOTTOTIPO[val]) {
SOTTOTIPO[val].forEach(function(s) { $('#fon-sottotipo').append('<option value="' + s + '">' + s + '</option>'); });
$('#fon-sottotipo-sub').show();
}
});
$('#fon-sottotipo').on('change', function() {
var val = $(this).val();
$('#fon-specificita').html('<option value="">— tutte —</option>');
$('#fon-specificita-sub').hide();
if (val && SPECIFICITA[val]) {
SPECIFICITA[val].forEach(function(s) { $('#fon-specificita').append('<option value="' + s + '">' + s + '</option>'); });
$('#fon-specificita-sub').show();
}
});
// =====================
// LISTENERS LETTERATURA
// =====================
bindGeo('let-');
$('#let-macrosezione').on('change', function() {
var val = $(this).val();
$('#let-mit-wrapper').toggle(val === 'Mitologia');
$('#let-folk-wrapper').toggle(val === 'Folklore');
if (val !== 'Mitologia') {
$('#let-mit-macro').val('');
$('#let-mit-specific').html('<option value="">— tutte —</option>');
$('#let-mit-sub').hide();
}
if (val !== 'Folklore') { $('#let-folk-sezione').val(''); }
});
$('#let-mit-macro').on('change', function() {
var val = $(this).val();
var sub = $('#let-mit-specific');
sub.html('<option value="">— tutte —</option>');
if (val && SUBMITS[val] && SUBMITS[val].length > 0) {
SUBMITS[val].forEach(function(s) { sub.append('<option value="' + s + '">' + s.replace('Mitologia ', '') + '</option>'); });
$('#let-mit-sub').show();
} else { $('#let-mit-sub').hide(); }
});
$('#let-tipologia').on('change', function() {
var val = $(this).val();
$('#let-sottotipo').html('<option value="">— tutte —</option>');
$('#let-specificita').html('<option value="">— tutte —</option>');
$('#let-sottotipo-sub,#let-specificita-sub').hide();
if (val && SOTTOTIPO[val]) {
SOTTOTIPO[val].forEach(function(s) { $('#let-sottotipo').append('<option value="' + s + '">' + s + '</option>'); });
$('#let-sottotipo-sub').show();
}
});
$('#let-sottotipo').on('change', function() {
var val = $(this).val();
$('#let-specificita').html('<option value="">— tutte —</option>');
$('#let-specificita-sub').hide();
if (val && SPECIFICITA[val]) {
SPECIFICITA[val].forEach(function(s) { $('#let-specificita').append('<option value="' + s + '">' + s + '</option>'); });
$('#let-specificita-sub').show();
}
});
// =====================
// LISTENERS MUSICA
// =====================
bindGeo('mus-');
$('#mus-macrosezione').on('change', function() {
var val = $(this).val();
$('#mus-mit-wrapper').toggle(val === 'Mitologia');
$('#mus-folk-wrapper').toggle(val === 'Folklore');
if (val !== 'Mitologia') {
$('#mus-mit-macro').val('');
$('#mus-mit-specific').html('<option value="">— tutte —</option>');
$('#mus-mit-sub').hide();
}
if (val !== 'Folklore') { $('#mus-folk-sezione').val(''); }
});
$('#mus-mit-macro').on('change', function() {
var val = $(this).val();
var sub = $('#mus-mit-specific');
sub.html('<option value="">— tutte —</option>');
if (val && SUBMITS[val] && SUBMITS[val].length > 0) {
SUBMITS[val].forEach(function(s) { sub.append('<option value="' + s + '">' + s.replace('Mitologia ', '') + '</option>'); });
$('#mus-mit-sub').show();
} else { $('#mus-mit-sub').hide(); }
});
$('#mus-tipologia').on('change', function() {
var val = $(this).val();
$('#mus-sottotipo').html('<option value="">— tutte —</option>');
$('#mus-specificita').html('<option value="">— tutte —</option>');
$('#mus-sottotipo-sub,#mus-specificita-sub').hide();
if (val && SOTTOTIPO[val]) {
SOTTOTIPO[val].forEach(function(s) { $('#mus-sottotipo').append('<option value="' + s + '">' + s + '</option>'); });
$('#mus-sottotipo-sub').show();
}
});
$('#mus-sottotipo').on('change', function() {
var val = $(this).val();
$('#mus-specificita').html('<option value="">— tutte —</option>');
$('#mus-specificita-sub').hide();
if (val && SPECIFICITA[val]) {
SPECIFICITA[val].forEach(function(s) { $('#mus-specificita').append('<option value="' + s + '">' + s + '</option>'); });
$('#mus-specificita-sub').show();
}
});
// =====================
// LISTENERS CINEMA
// =====================
bindGeo('cin-');
$('#cin-macrosezione').on('change', function() {
var val = $(this).val();
$('#cin-mit-wrapper').toggle(val === 'Mitologia');
$('#cin-folk-wrapper').toggle(val === 'Folklore');
if (val !== 'Mitologia') {
$('#cin-mit-macro').val('');
$('#cin-mit-specific').html('<option value="">— tutte —</option>');
$('#cin-mit-sub').hide();
}
if (val !== 'Folklore') { $('#cin-folk-sezione').val(''); }
});
$('#cin-mit-macro').on('change', function() {
var val = $(this).val();
var sub = $('#cin-mit-specific');
sub.html('<option value="">— tutte —</option>');
if (val && SUBMITS[val] && SUBMITS[val].length > 0) {
SUBMITS[val].forEach(function(s) { sub.append('<option value="' + s + '">' + s.replace('Mitologia ', '') + '</option>'); });
$('#cin-mit-sub').show();
} else { $('#cin-mit-sub').hide(); }
});
$('#cin-tipologia').on('change', function() {
var val = $(this).val();
$('#cin-sottotipo').html('<option value="">— tutte —</option>');
$('#cin-specificita').html('<option value="">— tutte —</option>');
$('#cin-sottotipo-sub,#cin-specificita-sub').hide();
if (val && SOTTOTIPO[val]) {
SOTTOTIPO[val].forEach(function(s) { $('#cin-sottotipo').append('<option value="' + s + '">' + s + '</option>'); });
$('#cin-sottotipo-sub').show();
}
});
$('#cin-sottotipo').on('change', function() {
var val = $(this).val();
$('#cin-specificita').html('<option value="">— tutte —</option>');
$('#cin-specificita-sub').hide();
if (val && SPECIFICITA[val]) {
SPECIFICITA[val].forEach(function(s) { $('#cin-specificita').append('<option value="' + s + '">' + s + '</option>'); });
$('#cin-specificita-sub').show();
}
});
// =====================
// LISTENERS MUSEO
// =====================
bindGeo('mus2-');
$('#mus2-macrosezione').on('change', function() {
var val = $(this).val();
$('#mus2-mit-wrapper').toggle(val === 'Mitologia');
$('#mus2-folk-wrapper').toggle(val === 'Folklore');
if (val !== 'Mitologia') {
$('#mus2-mit-macro').val('');
$('#mus2-mit-specific').html('<option value="">— tutte —</option>');
$('#mus2-mit-sub').hide();
}
if (val !== 'Folklore') { $('#mus2-folk-sezione').val(''); }
});
$('#mus2-mit-macro').on('change', function() {
var val = $(this).val();
var sub = $('#mus2-mit-specific');
sub.html('<option value="">— tutte —</option>');
if (val && SUBMITS[val] && SUBMITS[val].length > 0) {
SUBMITS[val].forEach(function(s) { sub.append('<option value="' + s + '">' + s.replace('Mitologia ', '') + '</option>'); });
$('#mus2-mit-sub').show();
} else { $('#mus2-mit-sub').hide(); }
});
$('#mus2-tipologia-mit').on('change', function() {
var val = $(this).val();
$('#mus2-sottotipo').html('<option value="">— tutte —</option>');
$('#mus2-specificita').html('<option value="">— tutte —</option>');
$('#mus2-sottotipo-sub,#mus2-specificita-sub').hide();
if (val && SOTTOTIPO[val]) {
SOTTOTIPO[val].forEach(function(s) { $('#mus2-sottotipo').append('<option value="' + s + '">' + s + '</option>'); });
$('#mus2-sottotipo-sub').show();
}
});
$('#mus2-sottotipo').on('change', function() {
var val = $(this).val();
$('#mus2-specificita').html('<option value="">— tutte —</option>');
$('#mus2-specificita-sub').hide();
if (val && SPECIFICITA[val]) {
SPECIFICITA[val].forEach(function(s) { $('#mus2-specificita').append('<option value="' + s + '">' + s + '</option>'); });
$('#mus2-specificita-sub').show();
}
});
// =====================
// CERCA
// =====================
$('#ri-btn-cerca').on('click', function() {
var at = $('#ri-area-tematica').val();
if (!at || at === 'voci') {
var macrosezione = $('#ri-macrosezione').val();
var altreCats = [];
var campoCategorie = {
'ri-continente': function(v) { return v; },
'ri-area': function(v) { return v; },
'ri-paese': function(v) { return v; },
'ri-regione': function(v) { return v; },
'ri-provincia': function(v) { return v; },
'ri-citta': function(v) { return v; },
'ri-sesso': function(v) { return 'Sesso: ' + v; },
'ri-aspetto': function(v) { return 'Aspetto: ' + v; },
'ri-indole': function(v) { return 'Indole: ' + v; },
'ri-elemento': function(v) { return 'Elemento: ' + v; },
'ri-habitat': function(v) { return 'Habitat: ' + v; },
'ri-fisici': function(v) { return 'Attributi Fisici: ' + v; },
'ri-abbigl': function(v) { return 'Abbigliamento: ' + v; },
'ri-armi': function(v) { return v; },
'ri-animali': function(v) { return v; },
'ri-minerali': function(v) { return v; },
'ri-vegetali': function(v) { return v; },
'ri-colori': function(v) { return v; },
'ri-numeri': function(v) { return v; },
'ri-altri': function(v) { return v; },
'ri-tematiche': function(v) { return v; }
};
$.each(campoCategorie, function(id, fn) { var v = $('#' + id).val(); if (v) altreCats.push(fn(v)); });
var tipoVal = $('#ri-specificita').val() || $('#ri-sottotipo').val() || $('#ri-tipo').val();
if (tipoVal) altreCats.push(tipoVal);
if (macrosezione === 'Mitologia & Folklore') {
if (altreCats.length === 0) {
$('#ri-empty').text('Seleziona almeno un altro filtro oltre a Mitologia & Folklore.').show();
return;
}
$('#ri-loading').show();
$('#ri-results').hide();
$('#ri-empty').hide();
$('#ri-tbody').empty();
$.when(
getCategoryMembers('Mitologia', '0'),
getCategoryMembers('Folklore', '0')
).done(function(resMit, resFolk) {
var union = [...new Set([...resMit, ...resFolk])];
var altrePromises = altreCats.map(function(c) { return getCategoryMembers(c, '0'); });
$.when.apply($, altrePromises).done(function() {
var args = Array.prototype.slice.call(arguments);
var sets = [union].concat(
altrePromises.length === 1 ? [args[0]] : args.map(function(a) { return Array.isArray(a) ? a : a[0]; })
);
var result = intersect(sets);
$('#ri-loading').hide();
if (result.length === 0) {
$('#ri-empty').text('Nessuna voce trovata con questi filtri.').show();
} else {
allResults = result;
loadedCount = 0;
$('#ri-count').text(result.length + ' voci trovate (mostro le prime ' + Math.min(LIMIT, result.length) + ')');
$('#ri-results').show();
loadBatch();
}
});
});
return;
} else {
var cats = altreCats.slice();
var mitVal = $('#ri-mit-specific').val() || $('#ri-mit-macro').val();
if (mitVal) cats.push(mitVal);
var folkVal = $('#ri-folk-sezione').val();
if (folkVal) cats.push(folkVal);
runSearch(cats, '0');
}
} else if (at === 'racconti') {
var cats = [];
var geoFields = ['rac-citta','rac-provincia','rac-regione','rac-paese','rac-area','rac-continente'];
for (var i = 0; i < geoFields.length; i++) {
var gv = $('#' + geoFields[i]).val();
if (gv) { cats.push(gv); break; }
}
var autore = $('#rac-autore').val();
if (autore) cats.push(autore);
var tipo = $('#rac-tipologia').val();
if (tipo) cats.push(tipo);
var atuN = $('#rac-atu-numero').val();
var atuG = $('#rac-atu-gruppo').val();
var atuM = $('#rac-atu-macro').val();
if (atuN) cats.push('ATU: ' + atuN);
else if (atuG) cats.push('ATU: ' + atuG);
else if (atuM) cats.push('ATU: ' + atuM);
($('#rac-at-motivi').val() || []).forEach(function(m) { cats.push('AT: ' + m); });
var tema = $('#rac-tematiche').val();
if (tema) cats.push(tema);
runSearch(cats, '516');
} else if (at === 'fonti') {
var cats = [];
// Geo: prende il valore più specifico compilato
var geoFieldsFon = ['fon-citta','fon-provincia','fon-regione','fon-paese','fon-area','fon-continente'];
for (var i = 0; i < geoFieldsFon.length; i++) {
var gv = $('#' + geoFieldsFon[i]).val();
if (gv) { cats.push(gv); break; }
}
// Autore (categoria diretta col nome dell'autore)
var autFon = $('#fon-autore').val();
if (autFon) cats.push(autFon);
// Tipo
var tipoFon = $('#fon-tipo').val();
if (tipoFon) cats.push(tipoFon);
// Forma (categoria diretta col nome della forma)
var formaFon = $('#fon-forma').val();
if (formaFon) cats.push(formaFon);
// Biblioteca
var bibFon = $('#fon-biblioteca').val();
if (bibFon) cats.push(bibFon);
// Sezione: sottosezione > sezione > macrosezione
var mitSubFon = $('#fon-mit-specific').val();
var mitMacFon = $('#fon-mit-macro').val();
var folkFon = $('#fon-folk-sezione').val();
var macroFon = $('#fon-macrosezione').val();
if (mitSubFon) cats.push(mitSubFon);
else if (mitMacFon) cats.push(mitMacFon);
else if (folkFon) cats.push(folkFon);
else if (macroFon) cats.push(macroFon);
// Tipologia
var tipoValFon = $('#fon-specificita').val() || $('#fon-sottotipo').val() || $('#fon-tipologia').val();
if (tipoValFon) cats.push(tipoValFon);
runSearch(cats, '518');
} else if (at === 'letteratura') {
var cats = [];
// Geo: valore più specifico
var geoFieldsLet = ['let-citta','let-provincia','let-regione','let-paese','let-area','let-continente'];
for (var i = 0; i < geoFieldsLet.length; i++) {
var gv = $('#' + geoFieldsLet[i]).val();
if (gv) { cats.push(gv); break; }
}
// Autore
var autLet = $('#let-autore').val();
if (autLet) cats.push(autLet);
// Forma
var formaLet = $('#let-forma').val();
if (formaLet) cats.push(formaLet);
// Genere
var genereLet = $('#let-genere').val();
if (genereLet) cats.push(genereLet);
// Biblioteca (categoria aggiunta dal template se "Si")
var bibLet = $('#let-biblioteca').val();
if (bibLet) cats.push(bibLet);
// Sezione: sottosezione > sezione > folklore > macrosezione
var mitSubLet = $('#let-mit-specific').val();
var mitMacLet = $('#let-mit-macro').val();
var folkLet = $('#let-folk-sezione').val();
var macroLet = $('#let-macrosezione').val();
if (mitSubLet) cats.push(mitSubLet);
else if (mitMacLet) cats.push(mitMacLet);
else if (folkLet) cats.push(folkLet);
else if (macroLet) cats.push(macroLet);
// Tipologia
var tipoValLet = $('#let-specificita').val() || $('#let-sottotipo').val() || $('#let-tipologia').val();
if (tipoValLet) cats.push(tipoValLet);
runSearch(cats, '520');
} else if (at === 'musica') {
var cats = [];
// Geo: valore più specifico
var geoFieldsMus = ['mus-citta','mus-provincia','mus-regione','mus-paese','mus-area','mus-continente'];
for (var i = 0; i < geoFieldsMus.length; i++) {
var gv = $('#' + geoFieldsMus[i]).val();
if (gv) { cats.push(gv); break; }
}
// Autore
var autMus = $('#mus-autore').val();
if (autMus) cats.push(autMus);
// Tipo (nota: il template ha un bug sulla riga categoria tipo, ma includiamo comunque)
var tipoMus = $('#mus-tipo').val();
if (tipoMus) cats.push(tipoMus);
// Genere
var genereMus = $('#mus-genere').val();
if (genereMus) cats.push(genereMus);
// Sezione: sottosezione > sezione > folklore > macrosezione
var mitSubMus = $('#mus-mit-specific').val();
var mitMacMus = $('#mus-mit-macro').val();
var folkMus = $('#mus-folk-sezione').val();
var macroMus = $('#mus-macrosezione').val();
if (mitSubMus) cats.push(mitSubMus);
else if (mitMacMus) cats.push(mitMacMus);
else if (folkMus) cats.push(folkMus);
else if (macroMus) cats.push(macroMus);
// Tipologia
var tipoValMus = $('#mus-specificita').val() || $('#mus-sottotipo').val() || $('#mus-tipologia').val();
if (tipoValMus) cats.push(tipoValMus);
runSearch(cats, '522');
} else if (at === 'cinema') {
var cats = [];
// Geo: valore più specifico
var geoFieldsCin = ['cin-citta','cin-provincia','cin-regione','cin-paese','cin-area','cin-continente'];
for (var i = 0; i < geoFieldsCin.length; i++) {
var gv = $('#' + geoFieldsCin[i]).val();
if (gv) { cats.push(gv); break; }
}
// Regista
var regCin = $('#cin-regista').val();
if (regCin) cats.push(regCin);
// Tipo
var tipoCin = $('#cin-tipo').val();
if (tipoCin) cats.push(tipoCin);
// Genere
var genereCin = $('#cin-genere').val();
if (genereCin) cats.push(genereCin);
// Sezione
var mitSubCin = $('#cin-mit-specific').val();
var mitMacCin = $('#cin-mit-macro').val();
var folkCin = $('#cin-folk-sezione').val();
var macroCin = $('#cin-macrosezione').val();
if (mitSubCin) cats.push(mitSubCin);
else if (mitMacCin) cats.push(mitMacCin);
else if (folkCin) cats.push(folkCin);
else if (macroCin) cats.push(macroCin);
// Tipologia
var tipoValCin = $('#cin-specificita').val() || $('#cin-sottotipo').val() || $('#cin-tipologia').val();
if (tipoValCin) cats.push(tipoValCin);
runSearch(cats, '524');
} else if (at === 'museo') {
var cats = [];
// Geo
var geoFieldsMus2 = ['mus2-citta','mus2-provincia','mus2-regione','mus2-paese','mus2-area','mus2-continente'];
for (var i = 0; i < geoFieldsMus2.length; i++) {
var gv = $('#' + geoFieldsMus2[i]).val();
if (gv) { cats.push(gv); break; }
}
// Autore
var autMus2 = $('#mus2-autore').val();
if (autMus2) cats.push(autMus2);
// Periodo
var periodoMus2 = $('#mus2-periodo').val();
if (periodoMus2) cats.push(periodoMus2);
// Tipologia
var tipMus2 = $('#mus2-tipologia').val();
if (tipMus2) cats.push(tipMus2);
// Tecnica
var tecMus2 = $('#mus2-tecnica').val();
if (tecMus2) cats.push(tecMus2);
// Stile
var stileMus2 = $('#mus2-stile').val();
if (stileMus2) cats.push(stileMus2);
// Ubicazione
var ubicMus2 = $('#mus2-ubicazione').val();
if (ubicMus2) cats.push(ubicMus2);
// Sezione: sottosezione > sezione > folklore > macrosezione
var mitSubMus2 = $('#mus2-mit-specific').val();
var mitMacMus2 = $('#mus2-mit-macro').val();
var folkMus2 = $('#mus2-folk-sezione').val();
var macroMus2 = $('#mus2-macrosezione').val();
if (mitSubMus2) cats.push(mitSubMus2);
else if (mitMacMus2) cats.push(mitMacMus2);
else if (folkMus2) cats.push(folkMus2);
else if (macroMus2) cats.push(macroMus2);
// Tipologia mitologica
var tipoValMus2 = $('#mus2-specificita').val() || $('#mus2-sottotipo').val() || $('#mus2-tipologia-mit').val();
if (tipoValMus2) cats.push(tipoValMus2);
runSearch(cats, '6');
}
});
// =====================
// RESET
// =====================
$('#ri-btn-reset').on('click', function() {
$('#ri-voci-panel select').val('');
$('#ri-tipo,#ri-sottotipo,#ri-specificita').val('');
$('#ri-sottotipo-sub,#ri-specificita-sub').hide();
$('#ri-mit-sub,#ri-mit-wrapper,#ri-folk-wrapper').hide();
$('#ri-area-sub,#ri-paese-sub,#ri-regione-sub,#ri-provincia-sub,#ri-citta-sub').hide();
$('#ri-racconti-panel select:not(#rac-at-motivi)').val('');
$('#rac-at-motivi option').prop('selected', false);
$('#rac-area-sub,#rac-paese-sub,#rac-regione-sub,#rac-provincia-sub,#rac-citta-sub').hide();
$('#rac-atu-gruppo-sub,#rac-atu-numero-sub').hide();
$('#ri-macrosezione').val('');
// Reset pannello Fonti
$('#ri-fonti-panel select').val('');
$('#fon-epoca').val('0');
$('#fon-sottotipo-sub,#fon-specificita-sub').hide();
$('#fon-mit-sub,#fon-mit-wrapper,#fon-folk-wrapper').hide();
$('#fon-area-sub,#fon-paese-sub,#fon-regione-sub,#fon-provincia-sub,#fon-citta-sub').hide();
// Reset pannello Letteratura
$('#ri-letteratura-panel select').val('');
$('#let-epoca').val('0');
$('#let-sottotipo-sub,#let-specificita-sub').hide();
$('#let-mit-sub,#let-mit-wrapper,#let-folk-wrapper').hide();
$('#let-area-sub,#let-paese-sub,#let-regione-sub,#let-provincia-sub,#let-citta-sub').hide();
// Reset pannello Musica
$('#ri-musica-panel select').val('');
$('#mus-epoca').val('0');
$('#mus-sottotipo-sub,#mus-specificita-sub').hide();
$('#mus-mit-sub,#mus-mit-wrapper,#mus-folk-wrapper').hide();
$('#mus-area-sub,#mus-paese-sub,#mus-regione-sub,#mus-provincia-sub,#mus-citta-sub').hide();
// Reset pannello Cinema
$('#ri-cinema-panel select').val('');
$('#cin-epoca').val('0');
$('#cin-sottotipo-sub,#cin-specificita-sub').hide();
$('#cin-mit-sub,#cin-mit-wrapper,#cin-folk-wrapper').hide();
$('#cin-area-sub,#cin-paese-sub,#cin-regione-sub,#cin-provincia-sub,#cin-citta-sub').hide();
// Reset pannello Museo
$('#ri-museo-panel select').val('');
$('#mus2-sottotipo-sub,#mus2-specificita-sub').hide();
$('#mus2-mit-sub,#mus2-mit-wrapper,#mus2-folk-wrapper').hide();
$('#mus2-area-sub,#mus2-paese-sub,#mus2-regione-sub,#mus2-provincia-sub,#mus2-citta-sub').hide();
$('#ri-results,#ri-empty').hide();
$('#ri-tbody').empty();
allResults = [];
loadedCount = 0;
});
});
// =====================
// FOOTER
// =====================
$(function() {
var pageName = mw.config.get('wgPageName');
var pageEncoded = encodeURIComponent(pageName);
var userName = mw.config.get('wgUserName');
var isLoggedIn = mw.config.get('wgUserId') !== 0 && mw.config.get('wgUserName') !== null;
var strumentiPersonali = '';
if (isLoggedIn) {
var userEncoded = encodeURIComponent(userName);
strumentiPersonali =
'<li><a href="/index.php?title=Utente:' + userEncoded + '">' + userName + '</a></li>' +
'<li><a href="/index.php?title=Discussioni_utente:' + userEncoded + '">Discussioni</a></li>' +
'<li><a href="/index.php?title=Speciale:Preferenze">Preferenze</a></li>' +
'<li><a href="/index.php?title=Speciale:OsservatiSpeciali">Osservati speciali</a></li>' +
'<li><a href="/index.php?title=Speciale:MieiContributi/' + userEncoded + '">Contributi</a></li>' +
'<li><a href="/index.php?title=Speciale:Esci">Esci</a></li>';
} else {
strumentiPersonali =
'<li><a href="/index.php?title=Speciale:Entra">Entra / Registrati</a></li>';
}
var footer = '<div id="custom-footer">' +
'<div class="footer-grid">' +
'<div class="footer-col"><h3>Strumenti personali</h3><ul>' + strumentiPersonali + '</ul></div>' +
'<div class="footer-col"><h3>Strumenti</h3><ul>' +
'<li><a href="/index.php?title=Speciale:PuntanoQui/' + pageEncoded + '">Puntano qui</a></li>' +
'<li><a href="/index.php?title=Speciale:ModificheCorrelate/' + pageEncoded + '">Modifiche correlate</a></li>' +
'<li><a href="/index.php?title=Speciale:Carica">Carica un file</a></li>' +
'<li><a href="/index.php?title=Carica_nel_Museo">Carica nel Museo</a></li>' +
'<li><a href="/index.php?title=Speciale:PagineSpeciali">Pagine speciali</a></li>' +
'<li><a href="/index.php?title=Speciale:UltimeModifiche">Ultime modifiche</a></li>' +
'<li><a href="/index.php?title=Speciale:TutteLePagine">Indice alfabetico</a></li>' +
'</ul></div>' +
'<div class="footer-col"><h3>Social e contatti</h3><ul class="footer-social">' +
'<li><a href="https://www.facebook.com/pages/Il-Crepuscolo-degli-D%C3%A8i/261465273891593" target="_blank"><img src="/images/facebook_26.png" alt="Facebook"> FACEBOOK</a></li>' +
'<li><a href="https://twitter.com/ilcrepuscolo" target="_blank"><img src="/images/twitter_26.png" alt="Twitter"> TWITTER</a></li>' +
'<li><a href="https://pinterest.com/ilcrepuscolo" target="_blank"><img src="/images/pinterest_26.png" alt="Pinterest"> PINTEREST</a></li>' +
'<li><a href="https://www.instagram.com/_ilcrepuscolodeglidei/" target="_blank"><img src="/images/instagram_26.png" alt="Instagram"> INSTAGRAM</a></li>' +
'<li><a href="https://ilcrepuscolodeglidei.tumblr.com" target="_blank"><img src="/images/tumblr_26.png" alt="Tumblr"> TUMBLR</a></li>' +
'<li><a href="https://www.anobii.com/groups/01fe0dba7ff2c5ed0b/" target="_blank"><img src="/images/anobii_26.png" alt="Anobii"> ANOBII</a></li>' +
'<li><a href="mailto:admin@ilcrepuscolodeglidei.it"><img src="/images/email_26.png" alt="Contatti"> CONTATTI</a></li>' +
'</ul></div>' +
'</div>' +
'<div class="footer-bottom">' +
'Il Crepuscolo degli Dei è un progetto ideato, realizzato e gestito da Cristian Filagrossi.<br>' +
'Tutti i diritti riservati 1999-2026<br>' +
'<a href="https://www.iubenda.com/privacy-policy/54211721" target="_blank">Privacy Policy</a>' +
' - <a href="/index.php?title=Il_Crepuscolo_degli_D%C3%A8i:Informazioni">Informazioni su Il Crepuscolo degli Dei</a>' +
' - <a href="/index.php?title=Il_Crepuscolo_degli_D%C3%A8i:Avvertenze_generali">Avvertenze</a>' +
'</div></div>';
$('.citizen-body-container').after(footer);
$('footer.citizen-page-footer').hide();
});
/* =========================================================
GEO UTILS - Funzioni condivise per la cascata geografica
Usate da Maschera:Voce e Maschera:Racconto
========================================================= */
( function ( $ ) {
'use strict';
var geoData = null;
var API = 'https://www.ilcrepuscolodeglidei.org/api.php';
window.AREE_MASCHERA = {
'Africa': ['Africa del Nord','Africa Occidentale','Africa Centrale','Africa Orientale','Africa del Sud'],
'Americhe': ['Nordamerica','Centroamerica','Sudamerica'],
'Asia': ['Asia Settentrionale','Asia Centrale','Asia Meridionale','Medio Oriente','Estremo Oriente','Sudest Asiatico'],
'Europa': ['Nordeuropa','Europa Centrale','Europa Occidentale','Mediterraneo',"Europa dell'Est"],
'Oceania': ['Oceania Vicina','Oceania Lontana'],
'Tutti': ['Africa del Nord','Africa Occidentale','Africa Centrale','Africa Orientale','Africa del Sud','Nordamerica','Centroamerica','Sudamerica','Asia Settentrionale','Asia Centrale','Asia Meridionale','Medio Oriente','Estremo Oriente','Sudest Asiatico','Nordeuropa','Europa Centrale','Europa Occidentale','Mediterraneo',"Europa dell'Est",'Oceania Vicina','Oceania Lontana','Artico','Antartico']
};
window.caricaGeoJson = function ( callback ) {
if ( geoData !== null ) { callback( geoData ); return; }
$.getJSON( API, {
action: 'query', prop: 'revisions', titles: 'MediaWiki:geo.json',
rvprop: 'content', rvslots: 'main', format: 'json'
}, function ( d ) {
try {
var pages = d.query.pages;
var testo = pages[ Object.keys( pages )[0] ].revisions[0].slots.main['*'];
geoData = JSON.parse( testo );
} catch ( e ) { geoData = {}; }
callback( geoData );
} ).fail( function () { geoData = {}; callback( geoData ); } );
};
window.getOrCreateProxy = function ( $campo, proxyKey ) {
var $proxy = $campo.data( proxyKey );
if ( !$proxy || !$proxy.length ) {
$proxy = $( '<select></select>' ).css( {
'width': $campo.outerWidth() + 'px',
'font-size': $campo.css( 'font-size' ),
'display': 'none'
} );
$campo.after( $proxy );
$campo.data( proxyKey, $proxy );
}
return $proxy;
};
window.mostraProxy = function ( $campo, $proxy, opzioni ) {
var valAttuale = $campo.val();
$proxy.empty().append( $( '<option>' ).val( '' ).text( '— seleziona —' ) );
$.each( opzioni, function ( i, v ) {
$proxy.append( $( '<option>' ).val( v ).text( v ) );
} );
if ( valAttuale && opzioni.indexOf( valAttuale ) !== -1 ) { $proxy.val( valAttuale ); }
$campo.hide();
$proxy.show();
};
window.nascondiProxy = function ( $campo, proxyKey ) {
var $proxy = $campo.data( proxyKey );
if ( $proxy && $proxy.length ) { $proxy.hide(); }
$campo.show();
};
// Funzione generica che gestisce la cascata geo per una maschera
// template: nome del template es. 'Voce', 'Racconto'
// coppie: array di oggetti { cont, area, paese, reg, prov, cit }
window.bindGeoMascheraGenerica = function ( template, coppie ) {
var sentinel = '[name="' + template + '[' + coppie[0].paese + ']"]';
if ( !$( sentinel ).length ) return;
window.caricaGeoJson( function ( geo ) {
$.each( coppie, function ( i, c ) {
var $cont = c.cont ? $( '[name="' + template + '[' + c.cont + ']"]' ) : $();
var $area = c.area ? $( '[name="' + template + '[' + c.area + ']"]' ) : $();
var $paese = $( '[name="' + template + '[' + c.paese + ']"]' );
var $reg = $( '[name="' + template + '[' + c.reg + ']"]' );
var $prov = $( '[name="' + template + '[' + c.prov + ']"]' );
var $cit = c.cit ? $( '[name="' + template + '[' + c.cit + ']"]' ) : $();
if ( !$paese.length ) return;
// Continente → filtra Area
if ( $cont.length && $area.length ) {
$cont.on( 'change', function () {
var val = $( this ).val();
var opzioniArea = val && window.AREE_MASCHERA[ val ] ? window.AREE_MASCHERA[ val ] : [];
$area.find( 'option' ).each( function () {
var v = $( this ).val();
if ( v === '' ) return;
$( this ).toggle( !opzioniArea.length || opzioniArea.indexOf( v ) !== -1 );
} );
if ( opzioniArea.length && opzioniArea.indexOf( $area.val() ) === -1 ) { $area.val( '' ); }
$paese.val( '' );
window.nascondiProxy( $reg, 'geo-reg-proxy' ); $reg.val( '' );
window.nascondiProxy( $prov, 'geo-prov-proxy' ); $prov.val( '' );
if ( $cit.length ) { window.nascondiProxy( $cit, 'geo-cit-proxy' ); $cit.val( '' ); }
} );
}
// Paese → Regione → Provincia → Città
$paese.on( 'change', function () {
var paese = $( this ).val();
window.nascondiProxy( $reg, 'geo-reg-proxy' ); $reg.val( '' );
window.nascondiProxy( $prov, 'geo-prov-proxy' ); $prov.val( '' );
if ( $cit.length ) { window.nascondiProxy( $cit, 'geo-cit-proxy' ); $cit.val( '' ); }
if ( paese && geo[ paese ] && Object.keys( geo[ paese ] ).length ) {
var $proxyReg = window.getOrCreateProxy( $reg, 'geo-reg-proxy' );
window.mostraProxy( $reg, $proxyReg, Object.keys( geo[ paese ] ) );
$proxyReg.off( 'change.geo' ).on( 'change.geo', function () {
var reg = $proxyReg.val();
$reg.val( reg );
window.nascondiProxy( $prov, 'geo-prov-proxy' ); $prov.val( '' );
if ( $cit.length ) { window.nascondiProxy( $cit, 'geo-cit-proxy' ); $cit.val( '' ); }
if ( paese && reg && geo[ paese ][ reg ] && Object.keys( geo[ paese ][ reg ] ).length ) {
var $proxyProv = window.getOrCreateProxy( $prov, 'geo-prov-proxy' );
window.mostraProxy( $prov, $proxyProv, Object.keys( geo[ paese ][ reg ] ) );
$proxyProv.off( 'change.geo' ).on( 'change.geo', function () {
var prov = $proxyProv.val();
$prov.val( prov );
if ( $cit.length ) { window.nascondiProxy( $cit, 'geo-cit-proxy' ); $cit.val( '' ); }
if ( paese && reg && prov && geo[ paese ][ reg ][ prov ] && geo[ paese ][ reg ][ prov ].length ) {
var $proxyCit = window.getOrCreateProxy( $cit, 'geo-cit-proxy' );
window.mostraProxy( $cit, $proxyCit, geo[ paese ][ reg ][ prov ] );
$proxyCit.off( 'change.geo' ).on( 'change.geo', function () {
$cit.val( $proxyCit.val() );
} );
}
} );
}
} );
}
} );
// Inizializzazione per modifica pagina esistente
if ( $paese.val() && geo[ $paese.val() ] ) {
$paese.trigger( 'change' );
}
} );
} );
};
}( jQuery ) );
/* Campi progressivi nella Maschera:Voce */
( function ( mw, $ ) {
'use strict';
var GRUPPI_SENZA_NUMERO = [
{ prefisso: 'sezione', max: 3 },
{ prefisso: 'sottosezione', max: 3 },
{ prefisso: 'continente', max: 2 },
{ prefisso: 'area', max: 2 },
{ prefisso: 'paese', max: 6 },
{ prefisso: 'regione', max: 6 },
{ prefisso: 'provincia', max: 6 },
{ prefisso: 'sesso', max: 3 },
{ prefisso: 'aspetto', max: 3 },
{ prefisso: 'elemento', max: 2 },
{ prefisso: 'habitat', max: 2 },
];
var GRUPPI_CON_NUMERO = [
{ prefisso: 'fratello', max: 50 },
{ prefisso: 'fratellastro', max: 50 },
{ prefisso: 'fisici', max: 10 },
{ prefisso: 'animali', max: 10 },
{ prefisso: 'piante', max: 10 },
{ prefisso: 'minerali', max: 10 },
{ prefisso: 'colori', max: 10 },
{ prefisso: 'numeri', max: 10 },
{ prefisso: 'alimenti', max: 10 },
{ prefisso: 'armi', max: 10 },
{ prefisso: 'abbigliamento', max: 10 },
{ prefisso: 'altri', max: 10 },
{ prefisso: 'personaggi', max: 15 },
{ prefisso: 'ambiti', max: 15 },
];
var GRUPPI_FIGLI = [
{ prefisso: 'figlio1.', max: 50 },
{ prefisso: 'figlio2.', max: 50 },
{ prefisso: 'figlio3.', max: 50 },
{ prefisso: 'figlio4.', max: 50 },
{ prefisso: 'figlio5.', max: 50 },
];
function getCampoSenzaNumero( prefisso, i ) {
var nomeCampo = i === 1 ? prefisso : prefisso + i;
return $( '[name="Voce[' + nomeCampo + ']"]' ).first();
}
function getCampoConNumero( prefisso, i ) {
return $( '[name="Voce[' + prefisso + i + ']"]' ).first();
}
function getCampoVoceTesto( nome ) {
return $( '[name="VoceTesto[' + nome + ']"]' ).first();
}
function getRiga( $campo ) {
return $campo.closest( 'tr' );
}
function setVisibile( $campo, visibile ) {
getRiga( $campo ).toggle( visibile );
}
function isCompilato( $campo ) {
var val = $campo.val();
return val !== '' && val !== null;
}
function resetCampo( $campo ) {
$campo.val( '' );
$campo.siblings( 'input.ui-autocomplete-input' ).val( '' );
$campo.closest( '.comboboxSpan, .oo-ui-widget' )
.find( 'input.oo-ui-inputWidget-input' ).val( '' );
}
function creaBottone( label ) {
return $( '<tr class="pf-aggiungi-row"><td colspan="2">' +
'<button type="button" class="pf-aggiungi-btn" ' +
'style="margin:2px 0 4px 0;padding:2px 10px;cursor:pointer;font-size:0.9em;">' +
'+ ' + label +
'</button></td></tr>' );
}
function initGruppo( prefisso, max, fnGetCampo, labelAggiungi ) {
var campi = [];
for ( var i = 1; i <= max; i++ ) {
var $c = fnGetCampo( prefisso, i );
if ( $c.length ) campi.push( $c );
}
if ( campi.length <= 1 ) return;
labelAggiungi = labelAggiungi || 'Aggiungi';
var $bottone = creaBottone( labelAggiungi );
var ultimoVisibile = 0;
for ( var j = 0; j < campi.length; j++ ) {
if ( isCompilato( campi[j] ) ) ultimoVisibile = j;
}
for ( var k = 0; k < campi.length; k++ ) {
setVisibile( campi[k], k <= ultimoVisibile );
}
function aggiornaBottone() {
$bottone.detach();
var ultimo = 0;
for ( var n = 0; n < campi.length; n++ ) {
if ( getRiga( campi[n] ).is( ':visible' ) ) ultimo = n;
}
if ( ultimo < campi.length - 1 && isCompilato( campi[ultimo] ) ) {
getRiga( campi[ultimo] ).after( $bottone );
}
}
aggiornaBottone();
$bottone.on( 'click', function () {
var ultimo = 0;
for ( var n = 0; n < campi.length; n++ ) {
if ( getRiga( campi[n] ).is( ':visible' ) ) ultimo = n;
}
if ( ultimo + 1 < campi.length ) {
setVisibile( campi[ultimo + 1], true );
aggiornaBottone();
}
} );
( function ( campiLocali ) {
for ( var n = 0; n < campiLocali.length; n++ ) {
( function ( idx ) {
campiLocali[idx].on( 'input change', function () {
if ( !isCompilato( $( this ) ) ) {
for ( var m = idx + 1; m < campiLocali.length; m++ ) {
resetCampo( campiLocali[m] );
setVisibile( campiLocali[m], false );
}
}
aggiornaBottone();
} );
} )( n );
}
} )( campi );
}
function initGenitori() {
var coppie = [];
for ( var i = 1; i <= 4; i++ ) {
var $padre = $( '[name="Voce[padre' + i + ']"]' ).first();
var $madre = $( '[name="Voce[madre' + i + ']"]' ).first();
if ( $padre.length || $madre.length ) {
coppie.push( { padre: $padre, madre: $madre } );
}
}
if ( coppie.length === 0 ) return;
var $bottone = creaBottone( 'Aggiungi coppia genitori' );
function aggiornaVisibilita() {
for ( var i = 0; i < coppie.length; i++ ) {
if ( i === 0 ) {
setVisibile( coppie[i].padre, true );
setVisibile( coppie[i].madre, true );
} else {
var precedente = coppie[i - 1];
var visibile = isCompilato( precedente.padre ) || isCompilato( precedente.madre );
var giaVisibile = getRiga( coppie[i].padre ).is( ':visible' ) ||
getRiga( coppie[i].madre ).is( ':visible' );
if ( !visibile ) {
for ( var m = i; m < coppie.length; m++ ) {
resetCampo( coppie[m].padre );
resetCampo( coppie[m].madre );
setVisibile( coppie[m].padre, false );
setVisibile( coppie[m].madre, false );
}
break;
}
setVisibile( coppie[i].padre, visibile || giaVisibile );
setVisibile( coppie[i].madre, visibile || giaVisibile );
}
}
$bottone.detach();
var ultimaCompilata = -1;
for ( var j = 0; j < coppie.length; j++ ) {
if ( isCompilato( coppie[j].padre ) || isCompilato( coppie[j].madre ) ) {
ultimaCompilata = j;
}
}
if ( ultimaCompilata >= 0 && ultimaCompilata < coppie.length - 1 ) {
var $rigaRef = getRiga( coppie[ultimaCompilata].madre ).length ?
getRiga( coppie[ultimaCompilata].madre ) :
getRiga( coppie[ultimaCompilata].padre );
$rigaRef.after( $bottone );
}
}
aggiornaVisibilita();
$bottone.on( 'click', function () {
for ( var i = 1; i < coppie.length; i++ ) {
if ( !getRiga( coppie[i].padre ).is( ':visible' ) ) {
setVisibile( coppie[i].padre, true );
setVisibile( coppie[i].madre, true );
aggiornaVisibilita();
break;
}
}
} );
for ( var j = 0; j < coppie.length; j++ ) {
coppie[j].padre.on( 'input change', aggiornaVisibilita );
coppie[j].madre.on( 'input change', aggiornaVisibilita );
}
}
function initConsorti() {
var consorti = [];
for ( var i = 1; i <= 5; i++ ) {
var $consorte = $( '[name="Voce[consorte' + i + ']"]' ).first();
var $primoFiglio = $( '[name="Voce[figlio' + i + '.1]"]' ).first();
if ( $consorte.length ) {
consorti.push( { consorte: $consorte, figlio: $primoFiglio } );
}
}
if ( consorti.length === 0 ) return;
var $bottone = creaBottone( 'Aggiungi consorte' );
function aggiornaVisibilita() {
setVisibile( consorti[0].consorte, true );
if ( consorti[0].figlio.length ) setVisibile( consorti[0].figlio, true );
for ( var i = 1; i < consorti.length; i++ ) {
var vis = isCompilato( consorti[i - 1].consorte );
var giaVis = getRiga( consorti[i].consorte ).is( ':visible' );
if ( !vis ) {
for ( var m = i; m < consorti.length; m++ ) {
resetCampo( consorti[m].consorte );
if ( consorti[m].figlio.length ) resetCampo( consorti[m].figlio );
setVisibile( consorti[m].consorte, false );
if ( consorti[m].figlio.length ) setVisibile( consorti[m].figlio, false );
}
break;
}
setVisibile( consorti[i].consorte, vis || giaVis );
if ( consorti[i].figlio.length ) setVisibile( consorti[i].figlio, vis || giaVis );
}
$bottone.detach();
var ultimoCompilato = -1;
for ( var j = 0; j < consorti.length; j++ ) {
if ( isCompilato( consorti[j].consorte ) ) ultimoCompilato = j;
}
if ( ultimoCompilato >= 0 && ultimoCompilato < consorti.length - 1 ) {
var $riga = consorti[ultimoCompilato].figlio.length ?
getRiga( consorti[ultimoCompilato].figlio ) :
getRiga( consorti[ultimoCompilato].consorte );
$riga.after( $bottone );
}
}
aggiornaVisibilita();
$bottone.on( 'click', function () {
for ( var i = 1; i < consorti.length; i++ ) {
if ( !getRiga( consorti[i].consorte ).is( ':visible' ) ) {
setVisibile( consorti[i].consorte, true );
if ( consorti[i].figlio.length ) setVisibile( consorti[i].figlio, true );
aggiornaVisibilita();
break;
}
}
} );
for ( var j = 0; j < consorti.length; j++ ) {
consorti[j].consorte.on( 'input change', aggiornaVisibilita );
}
}
function initMitoSub() {
var coppie = [];
for ( var i = 1; i <= 15; i++ ) {
var $titolo = getCampoVoceTesto( 'mito_sub' + i + '_titolo' );
var $contenuto = getCampoVoceTesto( 'mito_sub' + i + '_contenuto' );
if ( $titolo.length || $contenuto.length ) {
coppie.push( { titolo: $titolo, contenuto: $contenuto } );
}
}
if ( coppie.length === 0 ) return;
var $bottone = creaBottone( 'Aggiungi sottoparagrafo' );
function aggiornaVisibilita() {
for ( var k = 0; k < coppie.length; k++ ) {
if ( k === 0 ) {
setVisibile( coppie[k].titolo, true );
setVisibile( coppie[k].contenuto, true );
} else {
var vis = isCompilato( coppie[k - 1].titolo );
var giaVis = getRiga( coppie[k].titolo ).is( ':visible' );
if ( !vis ) {
for ( var m = k; m < coppie.length; m++ ) {
resetCampo( coppie[m].titolo );
resetCampo( coppie[m].contenuto );
setVisibile( coppie[m].titolo, false );
setVisibile( coppie[m].contenuto, false );
}
break;
}
setVisibile( coppie[k].titolo, vis || giaVis );
setVisibile( coppie[k].contenuto, vis || giaVis );
}
}
$bottone.detach();
var ultimoCompilato = -1;
for ( var j = 0; j < coppie.length; j++ ) {
if ( isCompilato( coppie[j].titolo ) ) ultimoCompilato = j;
}
if ( ultimoCompilato >= 0 && ultimoCompilato < coppie.length - 1 ) {
getRiga( coppie[ultimoCompilato].contenuto ).after( $bottone );
}
}
aggiornaVisibilita();
$bottone.on( 'click', function () {
for ( var i = 1; i < coppie.length; i++ ) {
if ( !getRiga( coppie[i].titolo ).is( ':visible' ) ) {
setVisibile( coppie[i].titolo, true );
setVisibile( coppie[i].contenuto, true );
aggiornaVisibilita();
break;
}
}
} );
for ( var j = 0; j < coppie.length; j++ ) {
coppie[j].titolo.on( 'input change', aggiornaVisibilita );
}
}
function initSezioniExtra() {
var coppie = [];
for ( var i = 1; i <= 5; i++ ) {
var $titolo = getCampoVoceTesto( 'extra' + i + '_titolo' );
var $contenuto = getCampoVoceTesto( 'extra' + i + '_contenuto' );
if ( $titolo.length || $contenuto.length ) {
coppie.push( { titolo: $titolo, contenuto: $contenuto } );
}
}
if ( coppie.length === 0 ) return;
var $bottone = creaBottone( 'Aggiungi sezione extra' );
function aggiornaVisibilita() {
for ( var k = 0; k < coppie.length; k++ ) {
if ( k === 0 ) {
setVisibile( coppie[k].titolo, true );
setVisibile( coppie[k].contenuto, true );
} else {
var vis = isCompilato( coppie[k - 1].titolo );
var giaVis = getRiga( coppie[k].titolo ).is( ':visible' );
if ( !vis ) {
for ( var m = k; m < coppie.length; m++ ) {
resetCampo( coppie[m].titolo );
resetCampo( coppie[m].contenuto );
setVisibile( coppie[m].titolo, false );
setVisibile( coppie[m].contenuto, false );
}
break;
}
setVisibile( coppie[k].titolo, vis || giaVis );
setVisibile( coppie[k].contenuto, vis || giaVis );
}
}
$bottone.detach();
var ultimoCompilato = -1;
for ( var j = 0; j < coppie.length; j++ ) {
if ( isCompilato( coppie[j].titolo ) ) ultimoCompilato = j;
}
if ( ultimoCompilato >= 0 && ultimoCompilato < coppie.length - 1 ) {
getRiga( coppie[ultimoCompilato].contenuto ).after( $bottone );
}
}
aggiornaVisibilita();
$bottone.on( 'click', function () {
for ( var i = 1; i < coppie.length; i++ ) {
if ( !getRiga( coppie[i].titolo ).is( ':visible' ) ) {
setVisibile( coppie[i].titolo, true );
setVisibile( coppie[i].contenuto, true );
aggiornaVisibilita();
break;
}
}
} );
for ( var j = 0; j < coppie.length; j++ ) {
coppie[j].titolo.on( 'input change', aggiornaVisibilita );
}
}
function init() {
$.each( GRUPPI_SENZA_NUMERO, function ( i, g ) {
initGruppo( g.prefisso, g.max, getCampoSenzaNumero, 'Aggiungi ' + g.prefisso );
} );
$.each( GRUPPI_CON_NUMERO, function ( i, g ) {
initGruppo( g.prefisso, g.max, getCampoConNumero, 'Aggiungi ' + g.prefisso );
} );
$.each( GRUPPI_FIGLI, function ( i, g ) {
initGruppo( g.prefisso, g.max, getCampoConNumero, 'Aggiungi figlio' );
} );
initGenitori();
initConsorti();
initMitoSub();
initSezioniExtra();
}
mw.hook( 'pf.formSetup' ).add( function () { init(); } );
$( function () { init(); } );
}( mw, jQuery ) );
/* Layout responsive colonne Maschera:Voce */
( function ( $ ) {
function aggiornaColonne() {
var $container = $( '.pf-columns-4' ).first();
if ( !$container.length ) return;
var larghezza = $container.width();
$( '.pf-columns-4 .pf-col' ).each( function () {
if ( larghezza >= 1200 ) {
$( this ).css( 'flex', '1 1 calc(25% - 1.5em)' );
} else if ( larghezza >= 700 ) {
$( this ).css( 'flex', '1 1 calc(50% - 1.5em)' );
} else {
$( this ).css( 'flex', '1 1 100%' );
}
} );
}
$( function () {
aggiornaColonne();
$( window ).on( 'resize', aggiornaColonne );
} );
mw.hook( 'pf.formSetup' ).add( function () {
aggiornaColonne();
} );
}( jQuery ) );
/* Fix combobox OOUI nelle colonne */
( function ( $ ) {
function fixCombobox() {
$( '.pf-col .comboboxSpan' ).each( function () {
var $span = $( this );
var $widget = $span.find( '.oo-ui-comboBoxInputWidget' );
var $field = $span.find( '.oo-ui-comboBoxInputWidget-field' );
var $input = $span.find( 'input.oo-ui-inputWidget-input' );
var $button = $span.find( '.oo-ui-comboBoxInputWidget-dropdownButton' );
if ( !$widget.length || !$field.length || !$input.length || !$button.length ) return;
$widget[0].style.setProperty( 'width', '100%', 'important' );
$widget[0].style.setProperty( 'min-width', '0', 'important' );
$field[0].style.setProperty( 'display', 'flex', 'important' );
$field[0].style.setProperty( 'width', '100%', 'important' );
$field[0].style.setProperty( 'flex', '1 1 auto', 'important' );
$input[0].style.setProperty( 'flex', '1 1 auto', 'important' );
$input[0].style.setProperty( 'width', 'auto', 'important' );
$input[0].style.setProperty( 'min-width', '0', 'important' );
$button[0].style.setProperty( 'flex', '0 0 auto', 'important' );
$button[0].style.setProperty( 'width', 'auto', 'important' );
var observer = new MutationObserver( function () {
$widget[0].style.setProperty( 'width', '100%', 'important' );
$widget[0].style.setProperty( 'min-width', '0', 'important' );
$field[0].style.setProperty( 'display', 'flex', 'important' );
$field[0].style.setProperty( 'width', '100%', 'important' );
} );
observer.observe( $widget[0], { attributes: true, attributeFilter: [ 'style' ] } );
} );
}
mw.hook( 'pf.formSetup' ).add( function () {
setTimeout( fixCombobox, 300 );
} );
$( function () {
setTimeout( fixCombobox, 300 );
} );
}( jQuery ) );
/* =========================================================
LOGICHE CONDIZIONALI MASCHERA:VOCE
========================================================= */
( function ( $ ) {
var valoriSezionePerMacro = {
'Mitologia': ['', 'Altre Mitologie', 'Mitologia Africana Tribale', 'Mitologia Afroamericana', 'Mitologia Artica', 'Mitologia Asiatica Tribale', 'Mitologia Baltica', 'Mitologia Basca', 'Mitologia Celtica', 'Mitologia Classica', 'Mitologia Egizia', 'Mitologia Germanica', 'Mitologia Indo-Iranica', 'Mitologia Islamica', 'Mitologia Medievale', 'Mitologia Mesopotamica', 'Mitologia Oceanica', 'Mitologia Orientale', 'Mitologia Pellerossa', 'Mitologia Precolombiana', 'Mitologia Semitica', 'Mitologia Slava', 'Mitologia Ugro-Finnica'],
'Folklore': ['', 'Creature Fantastiche', 'Itinerari Arcani', 'Misteri', 'Simboli']
};
var valoriSottosezionePerSezione = {
'Mitologia Africana Tribale': ['', 'Mitologia Akamba', 'Mitologia Akan', 'Mitologia Ashanti', 'Mitologia Bakongo', 'Mitologia Bambara', 'Mitologia Cabila', 'Mitologia Dinka', 'Mitologia Dogon', 'Mitologia Efik', 'Mitologia Ewe', 'Mitologia Fon', 'Mitologia Khoi', 'Mitologia Khoisan', 'Mitologia Kikuyu', 'Mitologia Lotuko', 'Mitologia Lugbara', 'Mitologia Malgascia', 'Mitologia Ngombe', 'Mitologia Odinani', 'Mitologia Ovambo', 'Mitologia Peul', 'Mitologia San', 'Mitologia Yoruba', 'Mitologia Zulu'],
'Mitologia Afroamericana': ['', 'Mitologia Candomblé', 'Mitologia Santera', 'Mitologia Voodoo'],
'Mitologia Artica': ['', 'Mitologia Ainu', 'Mitologia Inuit', 'Mitologia Lappone'],
'Mitologia Asiatica Tribale': ['', 'Mitologia Filippina', 'Mitologia Indonesiana', 'Mitologia Kazaka', 'Mitologia Malese', 'Mitologia Tibetana', 'Mitologia Turco-Mongola'],
'Mitologia Baltica': ['', 'Mitologia Lettone', 'Mitologia Lituana'],
'Mitologia Celtica': ['', 'Mitologia Basca', 'Mitologia Britannica', 'Mitologia Gallese', 'Mitologia Gallica', 'Mitologia Irlandese'],
'Mitologia Classica': ['', 'Mitologia Etrusca', 'Mitologia Greca', 'Mitologia Italica', 'Mitologia Romana'],
'Mitologia Germanica': ['', 'Mitologia Germanica Continentale', 'Mitologia Nibelungica', 'Mitologia Norrena'],
'Mitologia Indo-Iranica': ['', 'Mitologia Buddhista', 'Mitologia Indiana', 'Mitologia Iranica', 'Mitologia Osseta', 'Mitologia Tamil'],
'Mitologia Medievale': ['', 'Mitologia Arturiana', 'Mitologia Carolingia', 'Mitologia Cristiana'],
'Mitologia Mesopotamica': ['', 'Mitologia Babilonese', 'Mitologia Ittita', 'Mitologia Sumera', 'Mitologia Urrita'],
'Mitologia Oceanica': ['', 'Mitologia Aborigena', 'Mitologia Maori', 'Mitologia Melanesiana', 'Mitologia Micronesiana', 'Mitologia Polinesiana'],
'Mitologia Orientale': ['', 'Mitologia Buddhista', 'Mitologia Cinese', 'Mitologia Coreana', 'Mitologia Giainista', 'Mitologia Giapponese', 'Mitologia Taoista'],
'Mitologia Pellerossa': ['', 'Mitologia Acoma', 'Mitologia Algonchina', 'Mitologia Alsea', 'Mitologia Arapaho', 'Mitologia Arikara', 'Mitologia Bella Coola', 'Mitologia Bilquala', 'Mitologia Caddo', 'Mitologia Cherokee', 'Mitologia Cheyenne', 'Mitologia Chickasaw', 'Mitologia Chinook', 'Mitologia Chipewyan', 'Mitologia Chiwere', 'Mitologia Choctaw', 'Mitologia Creek', 'Mitologia Crow', 'Mitologia Dakota', 'Mitologia Diegueño', 'Mitologia Haida', 'Mitologia Hokan', 'Mitologia Hopi', 'Mitologia Iowa', 'Mitologia Irochese', 'Mitologia Keres', 'Mitologia Kiowa', 'Mitologia Kwa', 'Mitologia Kwakiutl', 'Mitologia Lakota', 'Mitologia Lenape', 'Mitologia Lillooet', 'Mitologia Maidu', 'Mitologia Maliseet', 'Mitologia Micmac', 'Mitologia Miskito', 'Mitologia Mohave', 'Mitologia Mohawk', 'Mitologia Montagnais', 'Mitologia Muskogi', 'Mitologia Nadene', 'Mitologia Natchez', 'Mitologia Navaho', 'Mitologia Navajo', 'Mitologia Nootka', 'Mitologia Oglala', 'Mitologia Ojibwa', 'Mitologia Okanagan', 'Mitologia Onondaga', 'Mitologia Passamaquoddy', 'Mitologia Pawnee', 'Mitologia Penuti', 'Mitologia Pericue', 'Mitologia Piedi Neri', 'Mitologia Pima', 'Mitologia Pomo', 'Mitologia Ponca', 'Mitologia Powhatan', 'Mitologia Pueblo', 'Mitologia Puget', 'Mitologia Salish', 'Mitologia Sauk', 'Mitologia Seminole', 'Mitologia Seneca', 'Mitologia Sioux', 'Mitologia Snohomish', 'Mitologia Tanaina', 'Mitologia Tewa', 'Mitologia Tiwa', 'Mitologia Tlingit', 'Mitologia Tongva', 'Mitologia Tsimshian', 'Mitologia Uroni', 'Mitologia Wakashan', 'Mitologia Washoe', 'Mitologia Wichita', 'Mitologia Winnebago', 'Mitologia Wintun', 'Mitologia Wiyot', 'Mitologia Yavapai', 'Mitologia Yuchi', 'Mitologia Yuki', 'Mitologia Zuni'],
'Mitologia Precolombiana': ['', 'Mitologia Amazzonica', 'Mitologia Azteca', 'Mitologia Chamacoco', 'Mitologia Guarani', 'Mitologia Inca', 'Mitologia Maya', 'Mitologia Taino'],
'Mitologia Semitica': ['', 'Mitologia Araba', 'Mitologia Ebraica', 'Mitologia Fenicia', 'Mitologia Gnostica'],
'Mitologia Slava': ['', 'Mitologia Slava Meridionale', 'Mitologia Slava Occidentale', 'Mitologia Slava Orientale'],
'Mitologia Ugro-Finnica': ['', 'Mitologia Estone', 'Mitologia Finnica', 'Mitologia Magiara'],
'Mitologia Egizia': ['', 'Mitologia Egizia'],
'Mitologia Islamica': ['', 'Mitologia Araba', 'Mitologia Islamica'],
'Altre Mitologie': ['', 'Mitologia Armena', 'Mitologia Mapuche']
};
// Mapping geografico per sezioni che non hanno sottosezioni
var mappaSezione = {
'Mitologia Egizia': { continenti: ['Africa'], aree: ['Africa del Nord'], paesi: ['Egitto'] },
'Mitologia Basca': { continenti: ['Europa'], aree: ['Europa Occidentale'], paesi: ['Spagna', 'Francia'] },
'Mitologia Islamica': { continenti: ['Asia'], aree: ['Medio Oriente'], paesi: [] }
};
var mappaSottosezione = {
'Mitologia Akamba': { continenti: ['Africa'], aree: ['Africa Orientale'], paesi: ['Kenya', 'Tanzania'] },
'Mitologia Ashanti': { continenti: ['Africa'], aree: ['Africa Occidentale'], paesi: ['Ghana'] },
'Mitologia Bakongo': { continenti: ['Africa'], aree: ['Africa Centrale'], paesi: ['Angola', 'Congo'] },
'Mitologia Bambara': { continenti: ['Africa'], aree: ['Africa Occidentale'], paesi: ['Mali'] },
'Mitologia Cabila': { continenti: ['Africa'], aree: ['Africa del Nord'], paesi: ['Algeria'] },
'Mitologia Dinka': { continenti: ['Africa'], aree: ['Africa del Nord'], paesi: ['Sudan'] },
'Mitologia Dogon': { continenti: ['Africa'], aree: ['Africa Occidentale'], paesi: ['Mali'] },
'Mitologia Efik': { continenti: ['Africa'], aree: ['Africa Occidentale'], paesi: ['Nigeria'] },
'Mitologia Ewe': { continenti: ['Africa'], aree: ['Africa Occidentale'], paesi: ['Benin', 'Ghana', 'Togo'] },
'Mitologia Fon': { continenti: ['Africa'], aree: ['Africa Occidentale'], paesi: ['Benin'] },
'Mitologia Khoi': { continenti: ['Africa'], aree: ['Africa del Sud'], paesi: ['Botswana', 'Namibia', 'Sudafrica'] },
'Mitologia Kikuyu': { continenti: ['Africa'], aree: ['Africa Orientale'], paesi: ['Kenya'] },
'Mitologia Lotuko': { continenti: ['Africa'], aree: ['Africa del Nord'], paesi: ['Sudan'] },
'Mitologia Lugbara': { continenti: ['Africa'], aree: ['Africa Orientale', 'Africa Centrale'], paesi: ['Congo', 'Uganda'] },
'Mitologia Malgascia': { continenti: ['Africa'], aree: ['Africa Orientale'], paesi: ['Madagascar'] },
'Mitologia Ngombe': { continenti: ['Africa'], aree: ['Africa Orientale'], paesi: ['Kenya'] },
'Mitologia Odinani': { continenti: ['Africa'], aree: ['Africa Occidentale'], paesi: ['Nigeria'] },
'Mitologia Ovambo': { continenti: ['Africa'], aree: ['Africa del Sud'], paesi: ['Namibia'] },
'Mitologia Peul': { continenti: ['Africa'], aree: ['Africa Occidentale'], paesi: ['Senegal', 'Nigeria', 'Guinea', 'Camerun', 'Mali', 'Burkina Faso'] },
'Mitologia San': { continenti: ['Africa'], aree: ['Africa del Sud'], paesi: ['Botswana', 'Namibia', 'Sudafrica'] },
'Mitologia Yoruba': { continenti: ['Africa'], aree: ['Africa Occidentale'], paesi: ['Nigeria'] },
'Mitologia Zulu': { continenti: ['Africa'], aree: ['Africa del Sud'], paesi: ['Sudafrica'] },
'Mitologia Candomblé': { continenti: ['Americhe'], aree: ['Sudamerica'], paesi: ['Brasile'] },
'Mitologia Santera': { continenti: ['Americhe'], aree: ['Centroamerica'], paesi: ['Cuba'] },
'Mitologia Voodoo': { continenti: ['Americhe'], aree: ['Centroamerica'], paesi: ['Haiti'] },
'Mitologia Ainu': { continenti: ['Asia'], aree: ['Artico'], paesi: ['Giappone'] },
'Mitologia Inuit': { continenti: ['Europa', 'Americhe'], aree: ['Nordeuropa', 'Nordamerica'], paesi: ['Canada', 'Groenlandia', 'Usa'] },
'Mitologia Lappone': { continenti: ['Europa'], aree: ['Nordeuropa'], paesi: ['Finlandia', 'Norvegia', 'Svezia'] },
'Mitologia Filippina': { continenti: ['Asia'], aree: ['Sudest Asiatico'], paesi: ['Filippine'] },
'Mitologia Indonesiana': { continenti: ['Asia'], aree: ['Sudest Asiatico'], paesi: ['Indonesia'] },
'Mitologia Kazaka': { continenti: ['Asia'], aree: ['Asia Settentrionale'], paesi: ['Kazakhstan'] },
'Mitologia Malese': { continenti: ['Asia'], aree: ['Sudest Asiatico'], paesi: ['Malaysia'] },
'Mitologia Tibetana': { continenti: ['Asia'], aree: ['Asia Centrale'], paesi: ['Tibet'] },
'Mitologia Turco-Mongola': { continenti: ['Asia'], aree: ['Asia Centrale'], paesi: ['Turchia', 'Mongolia', 'Kazakhstan', 'Uzbekistan', 'Cina'] },
'Mitologia Lettone': { continenti: ['Europa'], aree: ["Europa dell'Est"], paesi: ['Lettonia'] },
'Mitologia Lituana': { continenti: ['Europa'], aree: ["Europa dell'Est"], paesi: ['Lituania'] },
'Mitologia Basca': { continenti: ['Europa'], aree: ['Europa Occidentale'], paesi: ['Spagna'] },
'Mitologia Britannica': { continenti: ['Europa'], aree: ['Nordeuropa'], paesi: ['Inghilterra'] },
'Mitologia Gallese': { continenti: ['Europa'], aree: ['Nordeuropa'], paesi: ['Galles'] },
'Mitologia Gallica': { continenti: ['Europa'], aree: ['Europa Centrale'], paesi: ['Francia'] },
'Mitologia Irlandese': { continenti: ['Europa'], aree: ['Nordeuropa'], paesi: ['Irlanda', 'Irlanda del Nord'] },
'Mitologia Etrusca': { continenti: ['Europa'], aree: ['Mediterraneo'], paesi: ['Italia'] },
'Mitologia Greca': { continenti: ['Europa'], aree: ['Mediterraneo'], paesi: ['Grecia'] },
'Mitologia Romana': { continenti: ['Europa'], aree: ['Mediterraneo'], paesi: ['Italia'] },
'Mitologia Egizia': { continenti: ['Africa'], aree: ['Africa del Nord'], paesi: ['Egitto'] },
'Mitologia Germanica Continentale': { continenti: ['Europa'], aree: ['Europa Centrale'], paesi: ['Germania'] },
'Mitologia Nibelungica': { continenti: ['Europa'], aree: ['Europa Centrale'], paesi: ['Germania'] },
'Mitologia Norrena': { continenti: ['Europa'], aree: ['Nordeuropa'], paesi: ['Norvegia', 'Danimarca', 'Svezia', 'Islanda'] },
'Mitologia Indiana': { continenti: ['Asia'], aree: ['Asia Meridionale'], paesi: ['India'] },
'Mitologia Iranica': { continenti: ['Asia'], aree: ['Medio Oriente'], paesi: ['Iran'] },
'Mitologia Osseta': { continenti: ['Asia'], aree: ['Asia Settentrionale'], paesi: ['Georgia', 'Russia'] },
'Mitologia Tamil': { continenti: ['Asia'], aree: ['Asia Meridionale'], paesi: ['India'] },
'Mitologia Araba': { continenti: ['Asia'], aree: ['Medio Oriente'], paesi: ['Arabia Saudita', 'Emirati Arabi Uniti', 'Oman', 'Yemen', 'Qatar', 'Bahrein'] },
'Mitologia Islamica': { continenti: ['Asia'], aree: ['Medio Oriente'], paesi: ['Arabia Saudita', 'Emirati Arabi Uniti', 'Oman', 'Yemen', 'Qatar', 'Bahrein'] },
'Mitologia Arturiana': { continenti: ['Europa'], aree: ['Nordeuropa'], paesi: ['Inghilterra', 'Galles'] },
'Mitologia Carolingia': { continenti: ['Europa'], aree: ['Europa Centrale'], paesi: ['Francia', 'Germania'] },
'Mitologia Cristiana': { continenti: ['Europa'], aree: ['Mediterraneo'], paesi: ['Italia'] },
'Mitologia Babilonese': { continenti: ['Asia'], aree: ['Medio Oriente'], paesi: ['Iraq'] },
'Mitologia Ittita': { continenti: ['Asia'], aree: ['Medio Oriente'], paesi: ['Turchia'] },
'Mitologia Sumera': { continenti: ['Asia'], aree: ['Medio Oriente'], paesi: ['Iraq'] },
'Mitologia Urrita': { continenti: ['Asia'], aree: ['Medio Oriente'], paesi: ['Iraq', 'Siria'] },
'Mitologia Aborigena': { continenti: ['Oceania'], aree: ['Oceania Vicina'], paesi: ['Australia'] },
'Mitologia Maori': { continenti: ['Oceania'], aree: ['Oceania Vicina'], paesi: ['Nuova Zelanda'] },
'Mitologia Melanesiana': { continenti: ['Oceania'], aree: ['Oceania Vicina'], paesi: ['Papua Nuova Guinea'] },
'Mitologia Micronesiana': { continenti: ['Oceania'], aree: ['Oceania Lontana'], paesi: ['Micronesia'] },
'Mitologia Polinesiana': { continenti: ['Oceania'], aree: ['Oceania Lontana'], paesi: ['Samoa', 'Tonga'] },
'Mitologia Buddhista': { continenti: ['Asia'], aree: ['Asia Meridionale'], paesi: ['India', 'Nepal'] },
'Mitologia Cinese': { continenti: ['Asia'], aree: ['Estremo Oriente'], paesi: ['Cina'] },
'Mitologia Coreana': { continenti: ['Asia'], aree: ['Estremo Oriente'], paesi: ['Corea del Sud', 'Corea del Nord'] },
'Mitologia Giainista': { continenti: ['Asia'], aree: ['Asia Meridionale'], paesi: ['India'] },
'Mitologia Giapponese': { continenti: ['Asia'], aree: ['Estremo Oriente'], paesi: ['Giappone'] },
'Mitologia Taoista': { continenti: ['Asia'], aree: ['Estremo Oriente'], paesi: ['Cina'] },
'Mitologia Ebraica': { continenti: ['Asia'], aree: ['Medio Oriente'], paesi: ['Israele'] },
'Mitologia Fenicia': { continenti: ['Asia'], aree: ['Medio Oriente'], paesi: ['Libano'] },
'Mitologia Gnostica': { continenti: ['Asia'], aree: ['Medio Oriente'], paesi: ['Israele', 'Egitto'] },
'Mitologia Slava Meridionale': { continenti: ['Europa'], aree: ['Mediterraneo'], paesi: ['Serbia', 'Croazia', 'Bosnia ed Erzegovina', 'Montenegro', 'Macedonia del Nord', 'Slovenia'] },
'Mitologia Slava Occidentale': { continenti: ['Europa'], aree: ['Europa Centrale'], paesi: ['Polonia', 'Repubblica Ceca'] },
'Mitologia Slava Orientale': { continenti: ['Europa'], aree: ["Europa dell'Est"], paesi: ['Russia', 'Ucraina', 'Bielorussia'] },
'Mitologia Estone': { continenti: ['Europa'], aree: ["Europa dell'Est"], paesi: ['Estonia'] },
'Mitologia Finnica': { continenti: ['Europa'], aree: ['Nordeuropa'], paesi: ['Finlandia'] },
'Mitologia Magiara': { continenti: ['Europa'], aree: ['Europa Centrale'], paesi: ['Ungheria'] }
};
var sottotipologiaPerTipologia = {
'Divinità': ['', 'Divinità delle Arti', 'Divinità delle Astrazioni', 'Divinità delle Attività', 'Divinità Infere', 'Divinità della Natura', 'Divinità Superne', 'Divinità Tutelari', 'Epiteti'],
'Semidèi': ['', 'Eroi', 'Santi', 'Epiteti'],
'Creature Fantastiche': ['', 'Animali', 'Esseri Intermediari', 'Mutanti', 'Non Morti', 'Piccolo Popolo', 'Umanoidi', 'Vegetali', 'Epiteti'],
'Umani': ['', 'Artigiani', 'Artisti', 'Fattori', 'Guerrieri', 'Istituzioni', 'Malviventi', 'Marinai', 'Nobili', 'Popoli', 'Religiosi', 'Scienziati', 'Servitori', 'Epiteti'],
'Luoghi': ['', 'Costruzioni', 'Luoghi Naturali', 'Luoghi Soprannaturali'],
'Oggetti': ['', 'Alimenti', 'Arredi', 'Armi', 'Corpi Celesti', 'Gioielli', 'Minerali', 'Oggetti Artistici', 'Recipienti', 'Strumenti Musicali', 'Veicoli', 'Altri Oggetti'],
'Concetti': ['', 'Concetti Sacri', 'Concetti Sociali'],
'Eventi': ['', 'Calendario', 'Combattimenti', 'Eventi Cosmici', 'Riti']
};
/* -------------------------------------------------------
Mapping condiviso: sottotipologia → opzioni specificità
Usato sia per Voce che per tutte le altre maschere
------------------------------------------------------- */
var specificitaPerSottotipologia = {
'Divinità delle Arti': ['', 'Divinità del Canto', 'Divinità della Danza', 'Divinità della Musica', 'Divinità della Poesia', 'Divinità della Scrittura'],
'Divinità delle Astrazioni': ['', "Divinità dell'Amicizia", "Divinità dell'Amore", "Divinità dell'Apostasia", 'Divinità della Conoscenza', 'Divinità del Coraggio', 'Divinità della Fame', 'Divinità del Fato', 'Divinità della Felicità', 'Divinità della Fortuna', 'Divinità della Forza', 'Divinità della Gelosia', 'Divinità dei Giuramenti', 'Divinità della Giustizia', "Divinità dell'Immortalità", "Divinità dell'Inganno", "Divinità dell'Invidia", 'Divinità della Longevità', 'Divinità delle Malattie', 'Divinità del Male', 'Divinità del Matrimonio', "Divinità dell'Odio", "Divinità dell'Onore", 'Divinità della Parola', 'Divinità della Pazzia', 'Divinità del Pensiero', 'Divinità del Piacere', 'Divinità della Pietà', 'Divinità della Povertà', 'Divinità della Prudenza', 'Divinità della Ricchezza', 'Divinità della Saggezza', 'Divinità della Salute', 'Divinità del Silenzio', 'Divinità del Sonno', 'Divinità della Speranza', 'Divinità dei Tabù', 'Divinità del Tempo', 'Divinità della Vecchiaia', 'Divinità della Vendetta', 'Divinità della Violenza', 'Divinità della Vittoria'],
'Divinità delle Attività': ['', "Divinità dell'Agricoltura", 'Divinità degli Artigiani', 'Divinità del Commercio', 'Divinità della Caccia', 'Divinità della Guerra', 'Divinità della Magia', 'Divinità della Medicina', 'Divinità della Pesca', 'Divinità del Sesso', 'Divinità del Viaggio'],
'Divinità Infere': ['', 'Divinità della Morte', "Divinità dell'Oltretomba"],
'Divinità della Natura': ['', "Divinità dell'Acqua", 'Divinità degli Alimenti', 'Divinità delle Alluvioni', 'Divinità degli Animali', 'Divinità della Fertilità', 'Divinità dei Fiumi', 'Divinità del Fuoco', 'Divinità del Mare', 'Divinità della Montagna', 'Divinità delle Stagioni', 'Divinità della Terra', 'Divinità dei Terremoti', 'Divinità della Vegetazione', 'Divinità dei Vulcani'],
'Divinità Superne': ['', "Divinità dell'Alba", "Divinità dell'Arcobaleno", 'Divinità del Cielo', 'Divinità della Creazione', 'Divinità della Grandine', 'Divinità delle Eclissi', 'Divinità della Luce', 'Divinità della Luna', 'Divinità della Neve', 'Divinità della Notte', 'Divinità dei Pianeti', 'Divinità della Pioggia', 'Divinità del Sole', 'Divinità delle Stelle', 'Divinità della Tempesta', 'Divinità del Tuono', 'Divinità del Vento'],
'Divinità Tutelari': ['', 'Divinità della Famiglia', 'Divinità della Nascita', 'Divinità dei Punti Cardinali', 'Divinità delle Strade', 'Divinità dei Villaggi'],
'Epiteti': ['', 'Epiteti Individuali', 'Epiteti Collettivi'],
'Eroi': ['', 'Eroi Classici', 'Eroi Culturali', 'Trickster'],
'Santi': [''],
'Animali': ['', 'Anfibi', 'Aracnidi', 'Bovini', 'Canidi', 'Caprini', 'Cetacei', 'Cervidi', 'Crostacei', 'Equini', 'Felini', 'Insetti', 'Leporidi', 'Molluschi', 'Pachidermi', 'Pesci', 'Pipistrelli', 'Primati', 'Rettili', 'Rinocerontidi', 'Roditori', 'Suini', 'Uccelli'],
'Esseri Intermediari': ['', 'Angeli', 'Demoni', 'Elementali'],
'Mutanti': ['', 'Mannari', 'Proteiformi'],
'Non Morti': ['', 'Fantasmi', 'Vampiri', 'Zombi'],
'Piccolo Popolo': ['', 'Elfi', 'Fate', 'Folletti', 'Gnomi', 'Nani'],
'Umanoidi': ['', 'Alieni', 'Automi', 'Giganti', 'Maghi&Streghe', 'Orchi', 'Popoli Favolosi', 'Uomini Selvaggi'],
'Vegetali': ['', 'Piante'],
'Artigiani': ['', 'Fabbri'],
'Artisti': ['', 'Musici', 'Poeti', 'Scultori'],
'Fattori': ['', 'Contadini', 'Pastori'],
'Guerrieri': ['', 'Araldi', 'Arcieri', 'Aurighi', 'Cavalieri', 'Condottieri', 'Fanti', 'Scudieri'],
'Istituzioni': ['', 'Funzionari', 'Governatori'],
'Malviventi': ['', 'Briganti', 'Ladri', 'Pirati'],
'Marinai': ['', 'Nocchieri'],
'Nobili': ['', 'Baroni', 'Conti', 'Duchi', 'Principi', 'Sovrani'],
'Popoli': [''],
'Religiosi': ['', 'Druidi', 'Monaci', 'Profeti', 'Sacerdoti', 'Sciamani'],
'Scienziati': [''],
'Servitori': ['', 'Ancelle', 'Nutrici'],
'Costruzioni': ['', 'Castelli', 'Chiese', 'Cimiteri', 'Città', 'Eremi', 'Fontane', 'Fortezze', 'Moschee', 'Palazzi', 'Ponti', 'Porte', 'Porti', 'Pozzi', 'Templi', 'Torri'],
'Luoghi Naturali': ['', 'Colline', 'Fiumi', 'Foreste', 'Grotte', 'Isole', 'Laghi', 'Mari', 'Montagne', 'Paludi', 'Regioni', 'Valli', 'Vulcani'],
'Luoghi Soprannaturali': ['', 'Dimore Divine', 'Luoghi Cosmici', 'Oltretombe'],
'Alimenti': ['', 'Bevande', 'Cibi'],
'Arredi': ['', 'Altari', 'Sedute', 'Specchi', 'Tavoli'],
'Armi': ['', 'Elmi', 'Lance', 'Martelli', 'Scudi', 'Spade'],
'Corpi Celesti': ['', 'Costellazioni', 'Pianeti', 'Stelle'],
'Gioielli': ['', 'Anelli', 'Bracciali', 'Collane'],
'Minerali': ['', 'Gemme', 'Pietre'],
'Oggetti Artistici': ['', 'Quadri', 'Statue'],
'Recipienti': ['', 'Calici', 'Pentole', 'Reliquari', 'Vasi'],
'Strumenti Musicali': ['', 'Strumenti a corda', 'Strumenti a fiato', 'Strumenti a percussione'],
'Veicoli': ['', 'Carri'],
'Altri Oggetti': ['', 'Bastone', 'Catena', 'Disco', 'Ruota'],
'Concetti Sacri': ['', 'Anima', 'Benedizione', 'Dio', 'Dottrina', 'Fato', 'Magia', 'Organizzazione Religiosa', 'Parola', 'Preghiera', 'Reincarnazione', 'Resurrezione', 'Tabù', 'Titolo Onorifico'],
'Concetti Sociali': ['', 'Organizzazione Sociale', 'Titolo Onorifico', 'Unità di Misura'],
'Calendario': ['', 'Ere', 'Giorni', 'Mesi'],
'Combattimenti': ['', 'Battaglie', 'Guerre'],
'Eventi Cosmici': [''],
'Riti': ['', 'Feste', 'Giochi', "Riti d'iniziazione", 'Sacrifici']
};
var registroAuto = {
continente: [], area: [], paese: [], regione: [], sesso: null
};
var tipologieNeutro = [ 'Luoghi', 'Oggetti', 'Eventi' ];
var sessoPerTipologia = {
'Creature Fantastiche': [ '', 'Non specificato', 'Maschio', 'Femmina', 'Neutro', 'Androgino' ],
'Umani': [ '', 'Non specificato', 'Maschio', 'Femmina', 'Neutro', 'Androgino' ]
};
var sezioniDaNascondere = [ 'CARATTERI', 'ATTRIBUTI', 'RELAZIONI' ];
var campiTestoDaNascondere = [ 'aspetto', 'aspetto2', 'aspetto3', 'indole', 'elemento', 'elemento2', 'habitat', 'habitat2' ];
var tipologieDisabilita = [ 'Luoghi', 'Oggetti', 'Eventi' ];
var tipologieNascondiElementoHabitat = [ 'Umani', 'Luoghi', 'Oggetti', 'Eventi' ];
function getNomeCampo( prefisso, indice ) {
return indice === 1 ? prefisso : prefisso + indice;
}
function setCampo( $campo, valore ) {
if ( !$campo.length ) return;
$campo.val( valore );
$campo.siblings( 'input.ui-autocomplete-input' ).val( valore );
var $span = $campo.closest( '.comboboxSpan' );
if ( $span.length ) { $span.find( 'input.oo-ui-inputWidget-input' ).val( valore ); }
var $widget = $campo.closest( '.oo-ui-widget' );
if ( $widget.length ) { $widget.find( 'input.oo-ui-inputWidget-input' ).val( valore ); }
$campo.closest( '.oo-ui-comboBoxInputWidget' ).find( 'input.oo-ui-inputWidget-input' ).val( valore );
$campo.closest( 'tr' ).toggle( !!valore );
}
function aggiornaSelect( $sel, nuoveOpzioni ) {
if ( !$sel.length ) return;
var valoreAttuale = $sel.val();
$sel.empty();
$.each( nuoveOpzioni, function ( i, val ) { $sel.append( $( '<option>' ).val( val ).text( val ) ); } );
$sel.val( nuoveOpzioni.indexOf( valoreAttuale ) !== -1 ? valoreAttuale : '' );
}
function ricalcolaGeografia() {
// Se la macrosezione è Folklore, i campi geografici sono liberi (nessun auto-fill)
var $macroGeo = $( '[name="Voce[macrosezione]"]' );
if ( $macroGeo.length && $macroGeo.val() === 'Folklore' ) { return; }
var campiSotto = [ 'sottosezione', 'sottosezione2', 'sottosezione3' ];
var campiSez = [ 'sezione', 'sezione2', 'sezione3' ];
var nuoviAuto = { continente: [], area: [], paese: [], regione: [] };
function applicaDati( dati ) {
$.each( dati.continenti || [], function ( j, v ) { if ( nuoviAuto.continente.indexOf( v ) === -1 ) nuoviAuto.continente.push( v ); } );
$.each( dati.aree || [], function ( j, v ) { if ( nuoviAuto.area.indexOf( v ) === -1 ) nuoviAuto.area.push( v ); } );
$.each( dati.paesi || [], function ( j, v ) { if ( nuoviAuto.paese.indexOf( v ) === -1 ) nuoviAuto.paese.push( v ); } );
$.each( dati.regioni || [], function ( j, v ) { if ( nuoviAuto.regione.indexOf( v ) === -1 ) nuoviAuto.regione.push( v ); } );
}
// Prima prova le sottosezioni
$.each( campiSotto, function ( i, nome ) {
var $c = $( '[name="Voce[' + nome + ']"]' );
if ( !$c.length ) return;
var val = $c.val();
if ( !val || !mappaSottosezione[ val ] ) return;
applicaDati( mappaSottosezione[ val ] );
} );
// Se nessuna sottosezione ha prodotto dati, prova le sezioni (per mitologie senza sottosezione)
if ( nuoviAuto.continente.length === 0 ) {
$.each( campiSez, function ( i, nome ) {
var $c = $( '[name="Voce[' + nome + ']"]' );
if ( !$c.length ) return;
var val = $c.val();
if ( !val || !mappaSezione[ val ] ) return;
applicaDati( mappaSezione[ val ] );
} );
}
var maxCampi = { continente: 2, area: 2, paese: 6, regione: 6 };
$.each( nuoviAuto, function ( prefisso, nuoviValori ) {
var max = maxCampi[ prefisso ], vecchiAuto = registroAuto[ prefisso ];
var valoriAttuali = [];
for ( var i = 1; i <= max; i++ ) {
var $c = $( '[name="Voce[' + getNomeCampo( prefisso, i ) + ']"]' );
valoriAttuali.push( $c.length ? ( $c.val() || '' ) : '' );
}
var valoriManuali = [];
$.each( valoriAttuali, function ( i, v ) { if ( v && vecchiAuto.indexOf( v ) === -1 && nuoviValori.indexOf( v ) === -1 ) valoriManuali.push( v ); } );
var listaFinale = nuoviValori.slice();
$.each( valoriManuali, function ( i, v ) { if ( listaFinale.indexOf( v ) === -1 ) listaFinale.push( v ); } );
for ( var j = 1; j <= max; j++ ) {
setCampo( $( '[name="Voce[' + getNomeCampo( prefisso, j ) + ']"]' ), listaFinale[ j - 1 ] || '' );
}
registroAuto[ prefisso ] = nuoviValori.slice();
} );
}
function aggiornaSezione( valMacro ) {
var $sezione = $( '[name="Voce[sezione]"]' );
if ( !$sezione.length ) return;
var valoreAttuale = $sezione.val();
var nuoviValori = valoriSezionePerMacro[ valMacro ] || [ '' ];
$sezione.empty();
$.each( nuoviValori, function ( i, val ) { $sezione.append( $( '<option>' ).val( val ).text( val ) ); } );
$sezione.val( nuoviValori.indexOf( valoreAttuale ) !== -1 ? valoreAttuale : '' );
// Folklore: mostra continente, area, paese, regione, provincia, città (solo primo campo)
// Mitologia: mostra sempre continente, area, paese (solo primo campo); nasconde regione, provincia, città
setTimeout( function () {
if ( valMacro === 'Folklore' ) {
var campiMostra = [ 'continente', 'area', 'paese', 'regione', 'provincia', 'città' ];
$.each( campiMostra, function ( i, prefisso ) {
var $c = $( '[name="Voce[' + prefisso + ']"]' ).first();
if ( !$c.length ) return;
$c.closest( 'tr' ).attr( 'style', '' );
} );
} else {
// Mitologia: mostra i primi tre campi geografici
var campiGeoMit = [ 'continente', 'area', 'paese' ];
$.each( campiGeoMit, function ( i, prefisso ) {
var $c = $( '[name="Voce[' + prefisso + ']"]' ).first();
if ( !$c.length ) return;
$c.closest( 'tr' ).attr( 'style', '' );
} );
// Nasconde regione, provincia e città (non pertinenti per Mitologia)
var campiProvCit = [ 'regione', 'regione2', 'regione3', 'regione4', 'regione5', 'regione6', 'provincia', 'provincia2', 'provincia3', 'provincia4', 'provincia5', 'provincia6', 'città' ];
$.each( campiProvCit, function ( i, nome ) {
var $c = $( '[name="Voce[' + nome + ']"]' ).first();
if ( !$c.length ) return;
$c.closest( 'tr' ).attr( 'style', 'display:none;' );
} );
}
aggiornaSottosezione();
}, 50 );
}
function aggiornaSottosezione() {
var campiSezione = [ 'sezione', 'sezione2', 'sezione3' ];
var campiSotto = [ 'sottosezione', 'sottosezione2', 'sottosezione3' ];
// Controlla se almeno una sezione è "Altre Mitologie"
var isAltreMitologie = false;
$.each( campiSezione, function ( i, nome ) {
var $s = $( '[name="Voce[' + nome + ']"]' );
if ( $s.length && $s.val() === 'Altre Mitologie' ) { isAltreMitologie = true; }
} );
// Gestione sottosezione: testo libero per Altre Mitologie, dropdown altrimenti
$.each( campiSotto, function ( i, nome ) {
var $s = $( '[name="Voce[' + nome + ']"]' );
if ( !$s.length ) return;
var $proxy = $s.data( 'altremit-proxy' );
if ( isAltreMitologie ) {
// Sostituisce il select con un input testo
if ( !$proxy || !$proxy.length ) {
$proxy = $( '<input type="text" size="40">' )
.css( { 'font-size': $s.css( 'font-size' ) } )
.val( $s.val() );
$proxy.on( 'input change', function () {
var v = $( this ).val();
if ( $s.find( 'option[value="' + v + '"]' ).length === 0 ) {
$s.find( 'option.altremit-custom' ).remove();
$s.append( $( '<option>' ).addClass( 'altremit-custom' ).val( v ).text( v ) );
}
$s.val( v );
} );
$s.data( 'altremit-proxy', $proxy );
$s.after( $proxy );
}
$s.hide();
$proxy.show();
} else {
// Ripristina il select
if ( $proxy && $proxy.length ) { $proxy.hide(); }
$s.show();
if ( $s.prop( 'tagName' ) === 'SELECT' ) {
var opzioniUnione = [ '' ];
$.each( campiSezione, function ( j, nomeSez ) {
var $sz = $( '[name="Voce[' + nomeSez + ']"]' );
if ( !$sz.length ) return;
var val = $sz.val();
if ( !val || !valoriSottosezionePerSezione[ val ] ) return;
$.each( valoriSottosezionePerSezione[ val ], function ( k, v ) {
if ( v && opzioniUnione.indexOf( v ) === -1 ) opzioniUnione.push( v );
} );
} );
aggiornaSelect( $s, opzioniUnione );
}
}
} );
}
function aggiornaSessoDaTipologia( valTipologia ) {
var $sesso = $( '[name="Voce[sesso]"]' );
if ( !$sesso.length ) return;
if ( tipologieNeutro.indexOf( valTipologia ) !== -1 ) {
if ( $sesso.find( 'option[value="Neutro"]' ).length === 0 ) { $sesso.append( $( '<option>' ).val( 'Neutro' ).text( 'Neutro' ) ); }
$sesso.val( 'Neutro' );
registroAuto.sesso = 'Neutro';
} else if ( sessoPerTipologia[ valTipologia ] ) {
aggiornaSelect( $sesso, sessoPerTipologia[ valTipologia ] );
if ( registroAuto.sesso !== null && $sesso.val() === registroAuto.sesso ) { $sesso.val( '' ); }
registroAuto.sesso = null;
} else {
if ( registroAuto.sesso !== null && $sesso.val() === registroAuto.sesso ) { $sesso.val( '' ); }
$sesso.find( 'option[value="Neutro"]' ).remove();
registroAuto.sesso = null;
}
}
function aggiornaSottotipologia( valTipologia ) {
var $sotto = $( '[name="Voce[sottotipologia]"]' );
if ( !$sotto.length ) return;
var nuoveOpzioni = sottotipologiaPerTipologia[ valTipologia ] || null;
if ( nuoveOpzioni ) { aggiornaSelect( $sotto, nuoveOpzioni ); }
aggiornaSpecificita( $sotto.val() );
}
/* FIX: il campo si chiama "specificità" con accento */
function aggiornaSpecificita( valSottotipologia ) {
var $spec = $( '[name="Voce[specificità]"]' );
if ( !$spec.length ) return;
var nuoveOpzioni = specificitaPerSottotipologia[ valSottotipologia ] || null;
if ( nuoveOpzioni ) { aggiornaSelect( $spec, nuoveOpzioni ); } else { $spec.val( '' ); }
}
function aggiornaSezioniFamiglia( valTipologia ) {
var nascondi = tipologieDisabilita.indexOf( valTipologia ) !== -1;
$( '.pf-col-header' ).each( function () {
if ( sezioniDaNascondere.indexOf( $( this ).text().trim() ) !== -1 ) { $( this ).closest( '.pf-col' ).toggle( !nascondi ); }
} );
$.each( campiTestoDaNascondere, function ( i, nome ) {
var $campo = $( '[name="VoceTesto[' + nome + ']"], [name="Voce[' + nome + ']"]' );
if ( $campo.length ) { $campo.closest( 'tr' ).toggle( !nascondi ); }
} );
}
function aggiornaElementoHabitat( valTipologia ) {
var nascondi = tipologieNascondiElementoHabitat.indexOf( valTipologia ) !== -1;
if ( nascondi ) {
$.each( [ 'elemento', 'elemento2', 'habitat', 'habitat2' ], function ( i, nome ) {
var $c = $( '[name="Voce[' + nome + ']"]' );
if ( $c.length ) { $c.closest( 'tr' ).hide(); }
} );
} else {
$.each( [ 'elemento', 'habitat' ], function ( i, nome ) {
var $c = $( '[name="Voce[' + nome + ']"]' );
if ( $c.length ) { $c.closest( 'tr' ).show(); }
} );
}
}
function initCondizionali() {
var $macro = $( '[name="Voce[macrosezione]"]' );
var $tipologia = $( '[name="Voce[tipologia]"]' );
var $sottotipo = $( '[name="Voce[sottotipologia]"]' );
var campiSotto = [ 'sottosezione', 'sottosezione2', 'sottosezione3' ];
if ( $macro.length ) aggiornaSezione( $macro.val() );
aggiornaSottosezione();
ricalcolaGeografia();
if ( $tipologia.length ) {
aggiornaSessoDaTipologia( $tipologia.val() );
aggiornaSottotipologia( $tipologia.val() );
aggiornaSezioniFamiglia( $tipologia.val() );
aggiornaElementoHabitat( $tipologia.val() );
}
if ( $sottotipo.length ) { aggiornaSpecificita( $sottotipo.val() ); }
$macro.on( 'change', function () { aggiornaSezione( $( this ).val() ); } );
$( '[name="Voce[sezione]"], [name="Voce[sezione2]"], [name="Voce[sezione3]"]' ).on( 'change', function () {
aggiornaSottosezione();
ricalcolaGeografia();
} );
$.each( campiSotto, function ( i, nome ) {
$( '[name="Voce[' + nome + ']"]' ).on( 'input change', function () { ricalcolaGeografia(); } );
} );
$tipologia.on( 'change', function () {
var val = $( this ).val();
aggiornaSessoDaTipologia( val );
aggiornaSottotipologia( val );
aggiornaSezioniFamiglia( val );
aggiornaElementoHabitat( val );
} );
$sottotipo.on( 'change', function () { aggiornaSpecificita( $( this ).val() ); } );
}
mw.hook( 'pf.formSetup' ).add( function () { initCondizionali(); } );
$( function () { initCondizionali(); } );
// =========================================================
// CASCATA GEOGRAFICA MASCHERA VOCE
// Continente → Area → Paese → Regione → Provincia → Città
// Regione, Provincia, Città vengono trasformati in combobox
// dinamici popolati dal geo.json
// =========================================================
// Usa le funzioni geo globali (window.geoUtils)
function bindGeoMaschera() {
window.bindGeoMascheraGenerica( 'Voce', [
{ cont: 'continente', area: 'area', paese: 'paese', reg: 'regione', prov: 'provincia', cit: 'città' },
{ cont: 'continente2', area: 'area2', paese: 'paese2', reg: 'regione2', prov: 'provincia2', cit: null }
] );
}
mw.hook( 'pf.formSetup' ).add( function () { setTimeout( bindGeoMaschera, 100 ); } );
$( function () { setTimeout( bindGeoMaschera, 100 ); } );
}( jQuery ) );
/* =========================================================
LOGICHE CONDIZIONALI TIPOLOGIA → SOTTOTIPOLOGIA → SPECIFICITÀ
Racconto, Fonte, Letteratura, Cinema, Musica
Stessa cascata a tre livelli della Maschera:Voce.
Se i campi non esistono nella pagina, tutto viene
ignorato silenziosamente.
========================================================= */
( function ( mw, $ ) {
'use strict';
var sottotipologiaPerTipologia = {
'Divinità': ['', 'Divinità delle Arti', 'Divinità delle Astrazioni', 'Divinità delle Attività', 'Divinità Infere', 'Divinità della Natura', 'Divinità Superne', 'Divinità Tutelari', 'Epiteti'],
'Semidèi': ['', 'Eroi', 'Santi', 'Epiteti'],
'Creature Fantastiche': ['', 'Animali', 'Esseri Intermediari', 'Mutanti', 'Non Morti', 'Piccolo Popolo', 'Umanoidi', 'Vegetali', 'Epiteti'],
'Umani': ['', 'Artigiani', 'Artisti', 'Fattori', 'Guerrieri', 'Istituzioni', 'Malviventi', 'Marinai', 'Nobili', 'Popoli', 'Religiosi', 'Scienziati', 'Servitori', 'Epiteti'],
'Luoghi': ['', 'Costruzioni', 'Luoghi Naturali', 'Luoghi Soprannaturali'],
'Oggetti': ['', 'Alimenti', 'Arredi', 'Armi', 'Corpi Celesti', 'Gioielli', 'Minerali', 'Oggetti Artistici', 'Recipienti', 'Strumenti Musicali', 'Veicoli', 'Altri Oggetti'],
'Concetti': ['', 'Concetti Sacri', 'Concetti Sociali'],
'Eventi': ['', 'Calendario', 'Combattimenti', 'Eventi Cosmici', 'Riti']
};
var specificitaPerSottotipologia = {
'Divinità delle Arti': ['', 'Divinità del Canto', 'Divinità della Danza', 'Divinità della Musica', 'Divinità della Poesia', 'Divinità della Scrittura'],
'Divinità delle Astrazioni': ['', "Divinità dell'Amicizia", "Divinità dell'Amore", "Divinità dell'Apostasia", 'Divinità della Conoscenza', 'Divinità del Coraggio', 'Divinità della Fame', 'Divinità del Fato', 'Divinità della Felicità', 'Divinità della Fortuna', 'Divinità della Forza', 'Divinità della Gelosia', 'Divinità dei Giuramenti', 'Divinità della Giustizia', "Divinità dell'Immortalità", "Divinità dell'Inganno", "Divinità dell'Invidia", 'Divinità della Longevità', 'Divinità delle Malattie', 'Divinità del Male', 'Divinità del Matrimonio', "Divinità dell'Odio", "Divinità dell'Onore", 'Divinità della Parola', 'Divinità della Pazzia', 'Divinità del Pensiero', 'Divinità del Piacere', 'Divinità della Pietà', 'Divinità della Povertà', 'Divinità della Prudenza', 'Divinità della Ricchezza', 'Divinità della Saggezza', 'Divinità della Salute', 'Divinità del Silenzio', 'Divinità del Sonno', 'Divinità della Speranza', 'Divinità dei Tabù', 'Divinità del Tempo', 'Divinità della Vecchiaia', 'Divinità della Vendetta', 'Divinità della Violenza', 'Divinità della Vittoria'],
'Divinità delle Attività': ['', "Divinità dell'Agricoltura", 'Divinità degli Artigiani', 'Divinità del Commercio', 'Divinità della Caccia', 'Divinità della Guerra', 'Divinità della Magia', 'Divinità della Medicina', 'Divinità della Pesca', 'Divinità del Sesso', 'Divinità del Viaggio'],
'Divinità Infere': ['', 'Divinità della Morte', "Divinità dell'Oltretomba"],
'Divinità della Natura': ['', "Divinità dell'Acqua", 'Divinità degli Alimenti', 'Divinità delle Alluvioni', 'Divinità degli Animali', 'Divinità della Fertilità', 'Divinità dei Fiumi', 'Divinità del Fuoco', 'Divinità del Mare', 'Divinità della Montagna', 'Divinità delle Stagioni', 'Divinità della Terra', 'Divinità dei Terremoti', 'Divinità della Vegetazione', 'Divinità dei Vulcani'],
'Divinità Superne': ['', "Divinità dell'Alba", "Divinità dell'Arcobaleno", 'Divinità del Cielo', 'Divinità della Creazione', 'Divinità della Grandine', 'Divinità delle Eclissi', 'Divinità della Luce', 'Divinità della Luna', 'Divinità della Neve', 'Divinità della Notte', 'Divinità dei Pianeti', 'Divinità della Pioggia', 'Divinità del Sole', 'Divinità delle Stelle', 'Divinità della Tempesta', 'Divinità del Tuono', 'Divinità del Vento'],
'Divinità Tutelari': ['', 'Divinità della Famiglia', 'Divinità della Nascita', 'Divinità dei Punti Cardinali', 'Divinità delle Strade', 'Divinità dei Villaggi'],
'Epiteti': ['', 'Epiteti Individuali', 'Epiteti Collettivi'],
'Eroi': ['', 'Eroi Classici', 'Eroi Culturali', 'Trickster'],
'Santi': [''],
'Animali': ['', 'Anfibi', 'Aracnidi', 'Bovini', 'Canidi', 'Caprini', 'Cetacei', 'Cervidi', 'Crostacei', 'Equini', 'Felini', 'Insetti', 'Leporidi', 'Molluschi', 'Pachidermi', 'Pesci', 'Pipistrelli', 'Primati', 'Rettili', 'Rinocerontidi', 'Roditori', 'Suini', 'Uccelli'],
'Esseri Intermediari': ['', 'Angeli', 'Demoni', 'Elementali'],
'Mutanti': ['', 'Mannari', 'Proteiformi'],
'Non Morti': ['', 'Fantasmi', 'Vampiri', 'Zombi'],
'Piccolo Popolo': ['', 'Elfi', 'Fate', 'Folletti', 'Gnomi', 'Nani'],
'Umanoidi': ['', 'Alieni', 'Automi', 'Giganti', 'Maghi&Streghe', 'Orchi', 'Popoli Favolosi', 'Uomini Selvaggi'],
'Vegetali': ['', 'Piante'],
'Artigiani': ['', 'Fabbri'],
'Artisti': ['', 'Musici', 'Poeti', 'Scultori'],
'Fattori': ['', 'Contadini', 'Pastori'],
'Guerrieri': ['', 'Araldi', 'Arcieri', 'Aurighi', 'Cavalieri', 'Condottieri', 'Fanti', 'Scudieri'],
'Istituzioni': ['', 'Funzionari', 'Governatori'],
'Malviventi': ['', 'Briganti', 'Ladri', 'Pirati'],
'Marinai': ['', 'Nocchieri'],
'Nobili': ['', 'Baroni', 'Conti', 'Duchi', 'Principi', 'Sovrani'],
'Popoli': [''],
'Religiosi': ['', 'Druidi', 'Monaci', 'Profeti', 'Sacerdoti', 'Sciamani'],
'Scienziati': [''],
'Servitori': ['', 'Ancelle', 'Nutrici'],
'Costruzioni': ['', 'Castelli', 'Chiese', 'Cimiteri', 'Città', 'Eremi', 'Fontane', 'Fortezze', 'Moschee', 'Palazzi', 'Ponti', 'Porte', 'Porti', 'Pozzi', 'Templi', 'Torri'],
'Luoghi Naturali': ['', 'Colline', 'Fiumi', 'Foreste', 'Grotte', 'Isole', 'Laghi', 'Mari', 'Montagne', 'Paludi', 'Regioni', 'Valli', 'Vulcani'],
'Luoghi Soprannaturali': ['', 'Dimore Divine', 'Luoghi Cosmici', 'Oltretombe'],
'Alimenti': ['', 'Bevande', 'Cibi'],
'Arredi': ['', 'Altari', 'Sedute', 'Specchi', 'Tavoli'],
'Armi': ['', 'Elmi', 'Lance', 'Martelli', 'Scudi', 'Spade'],
'Corpi Celesti': ['', 'Costellazioni', 'Pianeti', 'Stelle'],
'Gioielli': ['', 'Anelli', 'Bracciali', 'Collane'],
'Minerali': ['', 'Gemme', 'Pietre'],
'Oggetti Artistici': ['', 'Quadri', 'Statue'],
'Recipienti': ['', 'Calici', 'Pentole', 'Reliquari', 'Vasi'],
'Strumenti Musicali': ['', 'Strumenti a corda', 'Strumenti a fiato', 'Strumenti a percussione'],
'Veicoli': ['', 'Carri'],
'Altri Oggetti': ['', 'Bastone', 'Catena', 'Disco', 'Ruota'],
'Concetti Sacri': ['', 'Anima', 'Benedizione', 'Dio', 'Dottrina', 'Fato', 'Magia', 'Organizzazione Religiosa', 'Parola', 'Preghiera', 'Reincarnazione', 'Resurrezione', 'Tabù', 'Titolo Onorifico'],
'Concetti Sociali': ['', 'Organizzazione Sociale', 'Titolo Onorifico', 'Unità di Misura'],
'Calendario': ['', 'Ere', 'Giorni', 'Mesi'],
'Combattimenti': ['', 'Battaglie', 'Guerre'],
'Eventi Cosmici': [''],
'Riti': ['', 'Feste', 'Giochi', "Riti d'iniziazione", 'Sacrifici']
};
/* -------------------------------------------------------
Helper: aggiorna le opzioni di un <select> preservando
il valore corrente se ancora compatibile
------------------------------------------------------- */
function aggiornaSelect( $sel, nuoveOpzioni ) {
if ( !$sel.length ) return;
var valoreAttuale = $sel.val();
$sel.empty();
$.each( nuoveOpzioni, function ( i, val ) {
$sel.append( $( '<option>' ).val( val ).text( val ) );
} );
$sel.val( nuoveOpzioni.indexOf( valoreAttuale ) !== -1 ? valoreAttuale : '' );
}
/* Helper: nome campo progressivo (i=1 → 'tipologia', i=2 → 'tipologia2', ...) */
function nomeCampo( prefisso, i ) {
return i === 1 ? prefisso : prefisso + i;
}
/* Helper: trova il primo <select> corrispondente tra i due prefissi template */
function trovaCampo( template, nomeCmp ) {
return $( '[name="' + template[0] + '[' + nomeCmp + ']"],' +
'[name="' + template[1] + '[' + nomeCmp + ']"]' ).first();
}
/* Aggiorna sottotipologiaN in base a tipologiaN, poi a cascata specificitàN */
function aggiornaSottotipologia( template, i ) {
var $tipo = trovaCampo( template, nomeCampo( 'tipologia', i ) );
var $sotto = trovaCampo( template, nomeCampo( 'sottotipologia', i ) );
if ( !$tipo.length || !$sotto.length ) return;
var nuoveOpzioni = sottotipologiaPerTipologia[ $tipo.val() ] || null;
if ( nuoveOpzioni ) {
aggiornaSelect( $sotto, nuoveOpzioni );
} else {
$sotto.val( '' );
}
/* Cascata: il cambio di sottotipologia aggiorna la specificità */
aggiornaSpecificita( template, i );
}
/* Aggiorna specificitàN in base a sottotipologiaN */
function aggiornaSpecificita( template, i ) {
var $sotto = trovaCampo( template, nomeCampo( 'sottotipologia', i ) );
var $spec = trovaCampo( template, nomeCampo( 'specificità', i ) );
if ( !$sotto.length || !$spec.length ) return;
var nuoveOpzioni = specificitaPerSottotipologia[ $sotto.val() ] || null;
if ( nuoveOpzioni ) {
aggiornaSelect( $spec, nuoveOpzioni );
} else {
$spec.val( '' );
}
}
/* Inizializza le logiche per una singola maschera */
function initLogicaMaschera( template, sentinel, maxTipo, maxSotto ) {
if ( !$( sentinel ).length ) return;
/* Inizializzazione al caricamento */
for ( var i = 1; i <= maxTipo; i++ ) {
aggiornaSottotipologia( template, i );
}
/* I campi sottotipologia extra (oltre maxTipo) pilotano solo specificità */
for ( var k = maxTipo + 1; k <= maxSotto; k++ ) {
aggiornaSpecificita( template, k );
}
/* Listener tipologiaN → sottotipologiaN → specificitàN */
for ( var j = 1; j <= maxTipo; j++ ) {
( function ( idx ) {
trovaCampo( template, nomeCampo( 'tipologia', idx ) )
.on( 'change', function () {
aggiornaSottotipologia( template, idx );
} );
trovaCampo( template, nomeCampo( 'sottotipologia', idx ) )
.on( 'change', function () {
aggiornaSpecificita( template, idx );
} );
} )( j );
}
/* Listener per i campi sottotipologia extra (solo → specificità) */
for ( var m = maxTipo + 1; m <= maxSotto; m++ ) {
( function ( idx ) {
trovaCampo( template, nomeCampo( 'sottotipologia', idx ) )
.on( 'change', function () {
aggiornaSpecificita( template, idx );
} );
} )( m );
}
}
/*
maxTipo = numero massimo di campi tipologia progressivi
maxSotto = numero massimo di campi sottotipologia/specificità progressivi
*/
var maschere = [
{
template: [ 'Racconto', 'Racconto' ],
sentinel: '[name="Racconto[continente]"]',
maxTipo: 8, maxSotto: 15
},
{
template: [ 'SchedaBiblioteca', 'SchedaBibliotecaTesto' ],
sentinel: '[name="SchedaBiblioteca[titolo]"]',
maxTipo: 8, maxSotto: 15
},
{
template: [ 'SchedaLetteratura', 'SchedaLetteraturaTesto' ],
sentinel: '[name="SchedaLetteratura[titolo]"]',
maxTipo: 8, maxSotto: 15
},
{
template: [ 'SchedaCinema', 'SchedaCinemaTesto' ],
sentinel: '[name="SchedaCinema[regista]"]',
maxTipo: 8, maxSotto: 15
},
{
template: [ 'SchedaMusica', 'SchedaMusicaTesto' ],
sentinel: '[name="SchedaMusica[album]"]',
maxTipo: 8, maxSotto: 15
}
];
function initTutte() {
$.each( maschere, function ( i, m ) {
initLogicaMaschera( m.template, m.sentinel, m.maxTipo, m.maxSotto );
} );
}
mw.hook( 'pf.formSetup' ).add( function () { initTutte(); } );
$( function () { initTutte(); } );
}( mw, jQuery ) );
// Gestione maschera namespace principale, Racconti, Fonti, Letteratura e Cinema
$(function() {
var action = mw.config.get('wgAction');
var namespace = mw.config.get('wgNamespaceNumber');
var exists = mw.config.get('wgArticleId') !== 0;
var pageName = mw.config.get('wgPageName');
var formPerNamespace = {
0: 'Voce',
516: 'Racconto',
518: 'Fonte',
520: 'Letteratura',
522: 'Musica',
524: 'Cinema'
};
if ( action !== 'edit' || !formPerNamespace[ namespace ] ) return;
if ( exists ) return;
// Bypass maschera con parametro ?nomaschera=1
var urlParams = new URLSearchParams( window.location.search );
if ( urlParams.get( 'nomaschera' ) === '1' ) return;
$.getJSON( mw.util.wikiScript( 'api' ), {
action: 'query',
titles: pageName,
prop: 'categories',
clcategories: 'Categoria:Pagine di Servizio',
format: 'json'
}, function( data ) {
var pages = data.query && data.query.pages || {};
var isServizio = false;
$.each( pages, function( id, page ) {
if ( page.categories && page.categories.length > 0 ) {
isServizio = true;
}
} );
if ( !isServizio ) {
window.location.replace(
mw.util.getUrl( 'Special:FormEdit/' + formPerNamespace[ namespace ] + '/' + pageName )
);
}
} );
});
// Aggiungi pulsante Maschera dopo che la pagina è completamente caricata
mw.hook( 'wikipage.content' ).add( function() {
var namespace = mw.config.get( 'wgNamespaceNumber' );
var exists = mw.config.get( 'wgArticleId' ) !== 0;
var action = mw.config.get( 'wgAction' );
var pageName = mw.config.get( 'wgPageName' );
if ( ( namespace !== 0 && namespace !== 516 && namespace !== 518 && namespace !== 520 && namespace !== 522 && namespace !== 524 ) || !exists || action !== 'view' ) return;
if ( $( '#ca-formedit' ).length ) return;
var formName = namespace === 516 ? 'Racconto' : namespace === 518 ? 'Fonte' : namespace === 520 ? 'Letteratura' : namespace === 522 ? 'Musica' : namespace === 524 ? 'Cinema' : 'Voce';
var url = mw.util.getUrl( 'Special:FormEdit/' + formName + '/' + pageName );
var $caEdit = $( '#ca-edit' );
var $veEdit = $( '#ca-ve-edit' );
if ( !$caEdit.length ) return;
$veEdit.find( 'a span:not(.citizen-ui-icon)' ).text( 'WikiEditor' );
$veEdit.find( 'a' ).css( 'border-radius', '2em 0 0 2em' );
var $li = $(
'<li class="mw-list-item" id="ca-formedit" style="order:99">' +
'<a href="' + url + '" title="Modifica con maschera" ' +
'class="citizen-cdx-button--size-large cdx-button cdx-button--fake-button ' +
'cdx-button--fake-button--enabled cdx-button--weight-primary cdx-button--action-progressive" ' +
'style="border-radius:0">' +
'<span class="citizen-ui-icon mw-ui-icon-wikimedia-puzzle"></span>' +
'<span>Maschera</span></a></li>'
);
$caEdit.removeClass( 'collapsible' );
$caEdit.find( 'a' ).empty()
.append( $( '<span class="citizen-ui-icon mw-ui-icon-wikimedia-wikiText"></span>' ) )
.append( $( '<span>' ).text( 'Sorgente' ).attr( 'id', 'ca-edit-label' ) );
$caEdit.css( 'order', '100' );
$caEdit.find( 'a' ).css( 'border-radius', '0 2em 2em 0' );
$caEdit.before( $li );
} );
/* Campi progressivi nella Maschera:Racconto */
( function ( mw, $ ) {
'use strict';
function getCampoRacconto( prefisso, i ) {
var nomeCampo = i === 1 ? prefisso : prefisso + i;
return $( '[name="Racconto[' + nomeCampo + ']"]' ).first();
}
function getRiga( $campo ) {
return $campo.closest( 'tr' );
}
function setVisibile( $campo, visibile ) {
getRiga( $campo ).toggle( visibile );
}
function isCompilato( $campo ) {
var val = $campo.val();
return val !== '' && val !== null;
}
function creaBottone( label ) {
return $( '<tr class="pf-aggiungi-row"><td colspan="2">' +
'<button type="button" class="pf-aggiungi-btn" ' +
'style="margin:2px 0 4px 0;padding:2px 10px;cursor:pointer;font-size:0.9em;">' +
'+ ' + label +
'</button></td></tr>' );
}
function initGruppoRacconto( prefisso, max, label ) {
var campi = [];
for ( var i = 1; i <= max; i++ ) {
var $c = getCampoRacconto( prefisso, i );
if ( $c.length ) campi.push( $c );
}
if ( campi.length <= 1 ) return;
var $bottone = creaBottone( label || 'Aggiungi ' + prefisso );
var ultimoVisibile = 0;
for ( var j = 0; j < campi.length; j++ ) {
if ( isCompilato( campi[j] ) ) ultimoVisibile = j;
}
for ( var k = 0; k < campi.length; k++ ) {
setVisibile( campi[k], k <= ultimoVisibile );
}
function aggiornaBottone() {
$bottone.detach();
var ultimo = 0;
for ( var n = 0; n < campi.length; n++ ) {
if ( getRiga( campi[n] ).is( ':visible' ) ) ultimo = n;
}
if ( ultimo < campi.length - 1 && isCompilato( campi[ultimo] ) ) {
getRiga( campi[ultimo] ).after( $bottone );
}
}
aggiornaBottone();
$bottone.on( 'click', function () {
var ultimo = 0;
for ( var n = 0; n < campi.length; n++ ) {
if ( getRiga( campi[n] ).is( ':visible' ) ) ultimo = n;
}
if ( ultimo + 1 < campi.length ) {
setVisibile( campi[ultimo + 1], true );
aggiornaBottone();
}
} );
for ( var n = 0; n < campi.length; n++ ) {
( function ( idx ) {
campi[idx].on( 'input change', function () {
aggiornaBottone();
} );
} )( n );
}
}
function initRacconto() {
// Controlla che siamo nella maschera Racconto
if ( !$( '[name="Racconto[continente]"]' ).length ) return;
initGruppoRacconto( 'continente', 2, 'Aggiungi continente' );
initGruppoRacconto( 'area', 2, 'Aggiungi area' );
initGruppoRacconto( 'paese', 3, 'Aggiungi paese' );
initGruppoRacconto( 'regione', 2, 'Aggiungi regione' );
initGruppoRacconto( 'provincia', 2, 'Aggiungi provincia' );
/* I campi AT sono at1…at10 (tutti con numero), diversamente dagli altri
che usano la convenzione prefisso / prefisso2 / prefisso3.
Usiamo una versione dedicata che cerca sempre at1…at10. */
(function() {
var campiAt = [];
for ( var i = 1; i <= 10; i++ ) {
var $c = $( '[name="Racconto[at' + i + ']"]' ).first();
if ( $c.length ) campiAt.push( $c );
}
if ( campiAt.length <= 1 ) return;
var $bottoneAt = creaBottone( 'Aggiungi motivo AT' );
var ultimoVisAt = 0;
for ( var j = 0; j < campiAt.length; j++ ) {
if ( isCompilato( campiAt[j] ) ) ultimoVisAt = j;
}
for ( var k = 0; k < campiAt.length; k++ ) {
setVisibile( campiAt[k], k <= ultimoVisAt );
}
function aggiornaBottoneAt() {
$bottoneAt.detach();
var ultimo = 0;
for ( var n = 0; n < campiAt.length; n++ ) {
if ( getRiga( campiAt[n] ).is( ':visible' ) ) ultimo = n;
}
if ( ultimo < campiAt.length - 1 && isCompilato( campiAt[ultimo] ) ) {
getRiga( campiAt[ultimo] ).after( $bottoneAt );
}
}
aggiornaBottoneAt();
$bottoneAt.on( 'click', function () {
var ultimo = 0;
for ( var n = 0; n < campiAt.length; n++ ) {
if ( getRiga( campiAt[n] ).is( ':visible' ) ) ultimo = n;
}
if ( ultimo + 1 < campiAt.length ) {
setVisibile( campiAt[ultimo + 1], true );
aggiornaBottoneAt();
}
} );
for ( var n = 0; n < campiAt.length; n++ ) {
( function ( idx ) {
campiAt[idx].on( 'input change', function () { aggiornaBottoneAt(); } );
} )( n );
}
})();
initGruppoRacconto( 'personaggi',15, 'Aggiungi personaggio' );
initGruppoRacconto( 'ambiti', 15, 'Aggiungi tematica' );
}
mw.hook( 'pf.formSetup' ).add( function () { setTimeout( initRacconto, 200 ); } );
$( function () { setTimeout( initRacconto, 200 ); } );
/* ═══════════════════════════════════════════════════════════════
ATU CASCADE: macrogruppo → gruppo → numero
Legge la struttura da MediaWiki:atu.json.
macrogruppo e gruppo sono input[type=text] nella maschera;
il Common.js li sostituisce visivamente con <select> dinamici
mantenendo sincronizzato il valore nel campo hidden originale.
═══════════════════════════════════════════════════════════════ */
function initAtuCascadeRacconto() {
if ( !$( '[name="Racconto[macrogruppo]"]' ).length ) return;
var $inpMacro = $( '[name="Racconto[macrogruppo]"]' );
var $inpGruppo = $( '[name="Racconto[gruppo]"]' );
var $inpNumero = $( '[name="Racconto[numero]"]' );
if ( !$inpMacro.length || !$inpGruppo.length ) return;
var API_BASE = ( typeof mw !== 'undefined' && mw.config )
? mw.config.get( 'wgScriptPath' ) + '/api.php'
: '/api.php';
function caricaAtu( cb ) {
var TTL = 30 * 60 * 1000;
if ( window._atuJsonCache && ( Date.now() - window._atuJsonCacheTs < TTL ) ) { cb( window._atuJsonCache ); return; }
$.getJSON( API_BASE, {
action: 'query', prop: 'revisions',
titles: 'MediaWiki:atu.json',
rvprop: 'content', rvslots: 'main', format: 'json'
} ).done( function ( d ) {
try {
var pages = d.query.pages;
var raw = pages[ Object.keys( pages )[0] ].revisions[0].slots.main['*'];
window._atuJsonCache = JSON.parse( raw ); window._atuJsonCacheTs = Date.now();
} catch ( e ) { window._atuJsonCache = {}; window._atuJsonCacheTs = Date.now(); }
cb( window._atuJsonCache );
} ).fail( function () { cb( {} ); } );
}
/* Sostituisce un input[type=text] con un <select> che ne sincronizza il valore */
function creaSelectSopra( $inp, opzioni, valoreCorrente, placeholder ) {
$inp.hide();
var $sel = $( '<select>' ).css( {
width: '100%', padding: '4px 6px', fontSize: '13px',
border: '1px solid #ccc', borderRadius: '4px', background: '#fff'
} );
$sel.append( $( '<option>' ).val( '' ).text( placeholder || '— seleziona —' ) );
opzioni.forEach( function ( o ) {
$sel.append( $( '<option>' ).val( o ).text( o ) );
} );
if ( valoreCorrente ) $sel.val( valoreCorrente );
$sel.on( 'change', function () { $inp.val( $( this ).val() ).trigger( 'change' ); } );
$inp.after( $sel );
return $sel;
}
caricaAtu( function ( dati ) {
if ( !dati || !Object.keys( dati ).length ) return;
var macroIniziale = $inpMacro.val();
var gruppoIniziale = $inpGruppo.val();
/* Crea select macrogruppo */
var macroOpzioni = Object.keys( dati );
var $selMacro = creaSelectSopra( $inpMacro, macroOpzioni, macroIniziale, '— seleziona macrogruppo —' );
/* Crea select gruppo (inizialmente vuoto) */
$inpGruppo.hide();
var $selGruppo = $( '<select>' ).css( {
width: '100%', padding: '4px 6px', fontSize: '13px',
border: '1px solid #ccc', borderRadius: '4px', background: '#fff'
} );
$selGruppo.append( $( '<option>' ).val( '' ).text( '— seleziona prima il macrogruppo —' ) );
$selGruppo.prop( 'disabled', true );
$selGruppo.on( 'change', function () { $inpGruppo.val( $( this ).val() ).trigger( 'change' ); } );
$inpGruppo.after( $selGruppo );
function riempiGruppo( macro, gruppoPrec ) {
$selGruppo.empty();
if ( !macro || !dati[ macro ] ) {
$selGruppo.append( $( '<option>' ).val( '' ).text( '— seleziona prima il macrogruppo —' ) );
$selGruppo.prop( 'disabled', true );
return;
}
$selGruppo.append( $( '<option>' ).val( '' ).text( '— seleziona gruppo —' ) );
Object.keys( dati[ macro ] ).forEach( function ( g ) {
$selGruppo.append( $( '<option>' ).val( g ).text( g ) );
} );
$selGruppo.prop( 'disabled', false );
if ( gruppoPrec ) $selGruppo.val( gruppoPrec );
$inpGruppo.val( $selGruppo.val() );
}
/* Popola subito se c'è già un valore */
riempiGruppo( macroIniziale, gruppoIniziale );
function riempiNumero( macro, gruppo ) {
if ( !$inpNumero.length ) return;
/* numero è sempre input[type=text]: proponiamo un datalist */
var dlId = 'atu-numero-dl';
var $dl = $( '#' + dlId );
if ( !$dl.length ) {
$dl = $( '<datalist>' ).attr( 'id', dlId );
$inpNumero.attr( 'list', dlId ).after( $dl );
}
$dl.empty();
$inpNumero.val( '' );
if ( !macro || !gruppo || !dati[ macro ] || !dati[ macro ][ gruppo ] ) return;
dati[ macro ][ gruppo ].forEach( function ( v ) {
$dl.append( $( '<option>' ).val( v.numero ).attr( 'label', v.titolo ) );
} );
}
$selMacro.on( 'change', function () {
riempiGruppo( $( this ).val(), '' );
riempiNumero( '', '' );
} );
$selGruppo.on( 'change', function () {
riempiNumero( $selMacro.val(), $( this ).val() );
} );
/* Popola il datalist numeri se c'è già un gruppo salvato */
if ( macroIniziale && gruppoIniziale ) {
riempiNumero( macroIniziale, gruppoIniziale );
}
} );
}
/* ═══════════════════════════════════════════════════════════════
AT AUTOCOMPLETE: ricerca con match parziale su at-motivi.json
Si applica ai campi at1…at10 della Maschera:Racconto
═══════════════════════════════════════════════════════════════ */
function initAtAutocompleteRacconto() {
if ( !$( '[name="Racconto[at1]"]' ).length ) return;
var API_BASE = ( typeof mw !== 'undefined' && mw.config )
? mw.config.get( 'wgScriptPath' ) + '/api.php'
: '/api.php';
function caricaAt( cb ) {
var TTL = 30 * 60 * 1000;
if ( window._atJsonCache && ( Date.now() - window._atJsonCacheTs < TTL ) ) { cb( window._atJsonCache ); return; }
$.getJSON( API_BASE, {
action: 'query', prop: 'revisions',
titles: 'MediaWiki:at-motivi.json',
rvprop: 'content', rvslots: 'main', format: 'json'
} ).done( function ( d ) {
try {
var pages = d.query.pages;
var raw = pages[ Object.keys( pages )[0] ].revisions[0].slots.main['*'];
window._atJsonCache = JSON.parse( raw ); window._atJsonCacheTs = Date.now();
} catch ( e ) { window._atJsonCache = []; window._atJsonCacheTs = Date.now(); }
cb( window._atJsonCache );
} ).fail( function () { cb( [] ); } );
}
function collegaAutocomplete( $input, dati ) {
/* Evita di agganciare più volte lo stesso campo */
if ( $input.data( 'at-ac-bound' ) ) return;
$input.data( 'at-ac-bound', true );
/* Dropdown agganciato al body per evitare overflow:hidden dei genitori */
var $dd = $( '<ul>' ).css( {
display: 'none', position: 'fixed', zIndex: 99999,
background: '#fff', border: '1px solid #bbb', borderRadius: '4px',
maxHeight: '220px', overflowY: 'auto', margin: 0, padding: 0,
listStyle: 'none', fontSize: '0.9em', boxShadow: '0 3px 10px rgba(0,0,0,.2)',
minWidth: '340px'
} );
$( 'body' ).append( $dd );
function posiziona() {
var r = $input[0].getBoundingClientRect();
$dd.css( { top: r.bottom + 2, left: r.left, width: r.width } );
}
function mostra( risultati ) {
$dd.empty();
if ( !risultati.length ) { $dd.hide(); return; }
posiziona();
risultati.forEach( function ( voce ) {
var $li = $( '<li>' ).text( voce ).css( {
padding: '4px 10px', cursor: 'pointer', borderBottom: '1px solid #f0f0f0'
} );
$li.on( 'mouseenter', function () { $( this ).css( 'background', '#eef3ff' ); } );
$li.on( 'mouseleave', function () { $( this ).css( 'background', '' ); } );
$li.on( 'mousedown', function ( e ) {
e.preventDefault();
$input.val( voce );
$dd.hide();
/* sposta il focus al campo successivo */
var $next = $input.closest( 'tr' ).next( 'tr' ).find( 'input[type=text], select' ).first();
if ( $next.length ) $next.focus();
} );
$dd.append( $li );
} );
$dd.show();
}
$input.on( 'input', function () {
var q = $.trim( $( this ).val() ).toLowerCase();
if ( !q ) { $dd.hide(); return; }
var res = dati.filter( function ( v ) {
return v.toLowerCase().indexOf( q ) !== -1;
} ).slice( 0, 50 );
mostra( res );
} );
$input.on( 'keydown', function ( e ) {
if ( e.key === 'Escape' ) { $dd.hide(); }
if ( e.key === 'Enter' ) {
var $primo = $dd.find( 'li' ).first();
if ( $primo.length ) { $primo.trigger( 'mousedown' ); e.preventDefault(); }
}
if ( e.key === 'ArrowDown' ) {
e.preventDefault();
$dd.find( 'li' ).first().focus();
}
} );
$input.on( 'blur', function () {
setTimeout( function () { $dd.hide(); }, 200 );
} );
/* Riposiziona se la pagina scrolla */
$( window ).on( 'scroll.atdd resize.atdd', posiziona );
}
caricaAt( function ( dati ) {
/* at (singolo, senza numero) e at1…at10 */
var nomi = [ 'at1', 'at2', 'at3', 'at4', 'at5', 'at6', 'at7', 'at8', 'at9', 'at10' ];
nomi.forEach( function ( nome ) {
var $campo = $( '[name="Racconto[' + nome + ']"]' );
if ( $campo.length && $campo.is( 'input' ) ) {
collegaAutocomplete( $campo, dati );
}
} );
} );
}
mw.hook( 'pf.formSetup' ).add( function () {
setTimeout( initAtuCascadeRacconto, 300 );
setTimeout( initAtAutocompleteRacconto, 300 );
} );
$( function () {
setTimeout( initAtuCascadeRacconto, 300 );
setTimeout( initAtAutocompleteRacconto, 300 );
} );
function bindGeoRacconto() {
window.bindGeoMascheraGenerica( 'Racconto', [
{ cont: 'continente', area: 'area', paese: 'paese', reg: 'regione', prov: 'provincia', cit: 'citta' },
{ cont: 'continente2', area: 'area2', paese: 'paese2', reg: 'regione2', prov: 'provincia2', cit: null },
{ cont: null, area: null, paese: 'paese3', reg: null, prov: null, cit: null }
] );
}
mw.hook( 'pf.formSetup' ).add( function () { setTimeout( bindGeoRacconto, 300 ); } );
$( function () { setTimeout( bindGeoRacconto, 300 ); } );
}( mw, jQuery ) );
/* Cascade geo Maschera:Cinema */
( function ( mw, $ ) {
'use strict';
function bindGeoCinema() {
window.bindGeoMascheraGenerica( 'SchedaCinema', [
{ cont: 'continente', area: 'area', paese: 'paese', reg: null, prov: null, cit: null },
{ cont: 'continente2', area: 'area2', paese: 'paese2', reg: null, prov: null, cit: null },
{ cont: 'continente3', area: 'area3', paese: 'paese3', reg: null, prov: null, cit: null },
{ cont: 'continente4', area: 'area4', paese: 'paese4', reg: null, prov: null, cit: null }
] );
}
mw.hook( 'pf.formSetup' ).add( function () { setTimeout( bindGeoCinema, 300 ); } );
$( function () { setTimeout( bindGeoCinema, 300 ); } );
}( mw, jQuery ) );
/* Cascade geo Maschera:Musica */
( function ( mw, $ ) {
'use strict';
function bindGeoMusica() {
window.bindGeoMascheraGenerica( 'SchedaMusica', [
{ cont: 'continente', area: 'area', paese: 'paese', reg: null, prov: null, cit: null },
{ cont: 'continente2', area: 'area2', paese: 'paese2', reg: null, prov: null, cit: null },
{ cont: 'continente3', area: 'area3', paese: 'paese3', reg: null, prov: null, cit: null },
{ cont: 'continente4', area: 'area4', paese: 'paese4', reg: null, prov: null, cit: null }
] );
}
mw.hook( 'pf.formSetup' ).add( function () { setTimeout( bindGeoMusica, 300 ); } );
$( function () { setTimeout( bindGeoMusica, 300 ); } );
}( mw, jQuery ) );
/* Cascade geo Maschera:Letteratura */
( function ( mw, $ ) {
'use strict';
function bindGeoLetteratura() {
window.bindGeoMascheraGenerica( 'SchedaLetteratura', [
{ cont: 'continente', area: 'area', paese: 'paese', reg: null, prov: null, cit: null },
{ cont: 'continente2', area: 'area2', paese: 'paese2', reg: null, prov: null, cit: null },
{ cont: 'continente3', area: 'area3', paese: 'paese3', reg: null, prov: null, cit: null },
{ cont: 'continente4', area: 'area4', paese: 'paese4', reg: null, prov: null, cit: null }
] );
}
mw.hook( 'pf.formSetup' ).add( function () { setTimeout( bindGeoLetteratura, 300 ); } );
$( function () { setTimeout( bindGeoLetteratura, 300 ); } );
}( mw, jQuery ) );
/* Cascade geo Maschera:Fonte */
( function ( mw, $ ) {
'use strict';
function bindGeoFonte() {
window.bindGeoMascheraGenerica( 'SchedaBiblioteca', [
{ cont: 'continente', area: 'area', paese: 'paese', reg: null, prov: null, cit: null },
{ cont: 'continente2', area: 'area2', paese: 'paese2', reg: null, prov: null, cit: null },
{ cont: 'continente3', area: 'area3', paese: 'paese3', reg: null, prov: null, cit: null },
{ cont: 'continente4', area: 'area4', paese: 'paese4', reg: null, prov: null, cit: null }
] );
}
mw.hook( 'pf.formSetup' ).add( function () { setTimeout( bindGeoFonte, 300 ); } );
$( function () { setTimeout( bindGeoFonte, 300 ); } );
}( mw, jQuery ) );
/* Campi progressivi nella Maschera:Letteratura */
( function ( mw, $ ) {
'use strict';
function getCampoLetteratura( prefisso, i, iniziaDaUno ) {
var nomeCampo = iniziaDaUno ? prefisso + i : ( i === 1 ? prefisso : prefisso + i );
var $campo = $( '[name="SchedaLetteratura[' + nomeCampo + ']"]' ).first();
if ( !$campo.length ) {
$campo = $( '[name="SchedaLetteraturaTesto[' + nomeCampo + ']"]' ).first();
}
return $campo;
}
function getRiga( $campo ) { return $campo.closest( 'tr' ); }
function setVisibile( $campo, visibile ) { getRiga( $campo ).toggle( visibile ); }
function isCompilato( $campo ) { var val = $campo.val(); return val !== '' && val !== null; }
function creaBottone( label ) {
return $( '<tr class="pf-aggiungi-row"><td colspan="2">' +
'<button type="button" class="pf-aggiungi-btn" ' +
'style="margin:2px 0 4px 0;padding:2px 10px;cursor:pointer;font-size:0.9em;">' +
'+ ' + label + '</button></td></tr>' );
}
function initGruppoLetteratura( prefisso, max, label, iniziaDaUno ) {
var campi = [];
for ( var i = 1; i <= max; i++ ) {
var $c = getCampoLetteratura( prefisso, i, iniziaDaUno );
if ( $c.length ) campi.push( $c );
}
if ( campi.length <= 1 ) return;
var $bottone = creaBottone( label || 'Aggiungi ' + prefisso );
var ultimoVisibile = 0;
for ( var j = 0; j < campi.length; j++ ) {
if ( isCompilato( campi[j] ) ) ultimoVisibile = j;
}
for ( var k = 0; k < campi.length; k++ ) {
setVisibile( campi[k], k <= ultimoVisibile );
}
function aggiornaBottone() {
$bottone.detach();
var ultimo = 0;
for ( var n = 0; n < campi.length; n++ ) {
if ( getRiga( campi[n] ).is( ':visible' ) ) ultimo = n;
}
if ( ultimo < campi.length - 1 && isCompilato( campi[ultimo] ) ) {
getRiga( campi[ultimo] ).after( $bottone );
}
}
aggiornaBottone();
$bottone.on( 'click', function () {
var ultimo = 0;
for ( var n = 0; n < campi.length; n++ ) {
if ( getRiga( campi[n] ).is( ':visible' ) ) ultimo = n;
}
if ( ultimo + 1 < campi.length ) {
setVisibile( campi[ultimo + 1], true );
aggiornaBottone();
}
} );
for ( var n = 0; n < campi.length; n++ ) {
( function ( idx ) {
campi[idx].on( 'input change', function () { aggiornaBottone(); } );
} )( n );
}
}
function initSezioniExtra() {
for ( var i = 1; i <= 5; i++ ) {
var $titolo = $( '[name="SchedaLetteraturaTesto[extra' + i + '_titolo]"]' ).first();
var $contenuto = $( '[name="SchedaLetteraturaTesto[extra' + i + '_contenuto]"]' ).first();
if ( !$titolo.length ) continue;
var $rigaTitolo = $titolo.closest( 'tr' );
var $rigaContenuto = $contenuto.closest( 'tr' );
if ( i === 1 ) {
$rigaTitolo.show();
$rigaContenuto.show();
continue;
}
if ( !$titolo.val() ) {
$rigaTitolo.hide();
$rigaContenuto.hide();
}
}
var $bottone = $( '<tr><td colspan="2"><button type="button" ' +
'style="margin:2px 0 4px 0;padding:2px 10px;cursor:pointer;font-size:0.9em;">' +
'+ Aggiungi sezione</button></td></tr>' );
function aggiornaBottoneExtra() {
$bottone.detach();
var ultimoVisibile = 0;
for ( var n = 1; n <= 5; n++ ) {
var $t = $( '[name="SchedaLetteraturaTesto[extra' + n + '_titolo]"]' ).first();
if ( $t.length && $t.closest( 'tr' ).is( ':visible' ) ) ultimoVisibile = n;
}
if ( ultimoVisibile < 5 ) {
var $contenutoUltimo = $( '[name="SchedaLetteraturaTesto[extra' + ultimoVisibile + '_contenuto]"]' ).first();
$contenutoUltimo.closest( 'tr' ).after( $bottone );
}
}
aggiornaBottoneExtra();
$bottone.on( 'click', function () {
var prossimo = 0;
for ( var n = 1; n <= 5; n++ ) {
var $t = $( '[name="SchedaLetteraturaTesto[extra' + n + '_titolo]"]' ).first();
if ( $t.length && $t.closest( 'tr' ).is( ':hidden' ) ) { prossimo = n; break; }
}
if ( prossimo ) {
$( '[name="SchedaLetteraturaTesto[extra' + prossimo + '_titolo]"]' ).closest( 'tr' ).show();
$( '[name="SchedaLetteraturaTesto[extra' + prossimo + '_contenuto]"]' ).closest( 'tr' ).show();
aggiornaBottoneExtra();
}
} );
}
function initLetteratura() {
if ( !$( '[name="SchedaLetteratura[titolo]"]' ).length ) return;
initGruppoLetteratura( 'continente', 4, 'Aggiungi continente' );
initGruppoLetteratura( 'area', 4, 'Aggiungi area' );
initGruppoLetteratura( 'paese', 4, 'Aggiungi paese' );
initGruppoLetteratura( 'macrosezione', 2, 'Aggiungi macrosezione' );
initGruppoLetteratura( 'sezione', 10, 'Aggiungi sezione' );
initGruppoLetteratura( 'sottosezione', 10, 'Aggiungi sottosezione' );
initGruppoLetteratura( 'tipologia', 8, 'Aggiungi tipologia' );
initGruppoLetteratura( 'sottotipologia',15, 'Aggiungi sottotipologia' );
initGruppoLetteratura( 'specificità', 15, 'Aggiungi specificità' );
initSezioniExtra();
}
mw.hook( 'pf.formSetup' ).add( function () { initLetteratura(); } );
$( function () { initLetteratura(); } );
}( mw, jQuery ) );
/* Campi progressivi nella Maschera:Fonte */
( function ( mw, $ ) {
'use strict';
function getCampoFonte( prefisso, i, iniziaDaUno ) {
var nomeCampo = iniziaDaUno ? prefisso + i : ( i === 1 ? prefisso : prefisso + i );
var $campo = $( '[name="SchedaBiblioteca[' + nomeCampo + ']"]' ).first();
if ( !$campo.length ) {
$campo = $( '[name="SchedaBibliotecaTesto[' + nomeCampo + ']"]' ).first();
}
return $campo;
}
function getRiga( $campo ) { return $campo.closest( 'tr' ); }
function setVisibile( $campo, visibile ) { getRiga( $campo ).toggle( visibile ); }
function isCompilato( $campo ) { var val = $campo.val(); return val !== '' && val !== null; }
function creaBottone( label ) {
return $( '<tr class="pf-aggiungi-row"><td colspan="2">' +
'<button type="button" class="pf-aggiungi-btn" ' +
'style="margin:2px 0 4px 0;padding:2px 10px;cursor:pointer;font-size:0.9em;">' +
'+ ' + label + '</button></td></tr>' );
}
function initGruppoFonte( prefisso, max, label, iniziaDaUno ) {
var campi = [];
for ( var i = 1; i <= max; i++ ) {
var $c = getCampoFonte( prefisso, i, iniziaDaUno );
if ( $c.length ) campi.push( $c );
}
if ( campi.length <= 1 ) return;
var $bottone = creaBottone( label || 'Aggiungi ' + prefisso );
var ultimoVisibile = 0;
for ( var j = 0; j < campi.length; j++ ) {
if ( isCompilato( campi[j] ) ) ultimoVisibile = j;
}
for ( var k = 0; k < campi.length; k++ ) {
setVisibile( campi[k], k <= ultimoVisibile );
}
function aggiornaBottone() {
$bottone.detach();
var ultimo = 0;
for ( var n = 0; n < campi.length; n++ ) {
if ( getRiga( campi[n] ).is( ':visible' ) ) ultimo = n;
}
if ( ultimo < campi.length - 1 && isCompilato( campi[ultimo] ) ) {
getRiga( campi[ultimo] ).after( $bottone );
}
}
aggiornaBottone();
$bottone.on( 'click', function () {
var ultimo = 0;
for ( var n = 0; n < campi.length; n++ ) {
if ( getRiga( campi[n] ).is( ':visible' ) ) ultimo = n;
}
if ( ultimo + 1 < campi.length ) {
setVisibile( campi[ultimo + 1], true );
aggiornaBottone();
}
} );
for ( var n = 0; n < campi.length; n++ ) {
( function ( idx ) {
campi[idx].on( 'input change', function () { aggiornaBottone(); } );
} )( n );
}
}
function initSezioniExtraFonte() {
for ( var i = 1; i <= 5; i++ ) {
var $titolo = $( '[name="SchedaBibliotecaTesto[extra' + i + '_titolo]"]' ).first();
var $contenuto = $( '[name="SchedaBibliotecaTesto[extra' + i + '_contenuto]"]' ).first();
if ( !$titolo.length ) continue;
var $rigaTitolo = $titolo.closest( 'tr' );
var $rigaContenuto = $contenuto.closest( 'tr' );
if ( i === 1 ) {
$rigaTitolo.show();
$rigaContenuto.show();
continue;
}
if ( !$titolo.val() ) {
$rigaTitolo.hide();
$rigaContenuto.hide();
}
}
var $bottone = $( '<tr><td colspan="2"><button type="button" ' +
'style="margin:2px 0 4px 0;padding:2px 10px;cursor:pointer;font-size:0.9em;">' +
'+ Aggiungi sezione</button></td></tr>' );
function aggiornaBottoneExtra() {
$bottone.detach();
var ultimoVisibile = 0;
for ( var n = 1; n <= 5; n++ ) {
var $t = $( '[name="SchedaBibliotecaTesto[extra' + n + '_titolo]"]' ).first();
if ( $t.length && $t.closest( 'tr' ).is( ':visible' ) ) ultimoVisibile = n;
}
if ( ultimoVisibile < 5 ) {
var $contenutoUltimo = $( '[name="SchedaBibliotecaTesto[extra' + ultimoVisibile + '_contenuto]"]' ).first();
$contenutoUltimo.closest( 'tr' ).after( $bottone );
}
}
aggiornaBottoneExtra();
$bottone.on( 'click', function () {
var prossimo = 0;
for ( var n = 1; n <= 5; n++ ) {
var $t = $( '[name="SchedaBibliotecaTesto[extra' + n + '_titolo]"]' ).first();
if ( $t.length && $t.closest( 'tr' ).is( ':hidden' ) ) { prossimo = n; break; }
}
if ( prossimo ) {
$( '[name="SchedaBibliotecaTesto[extra' + prossimo + '_titolo]"]' ).closest( 'tr' ).show();
$( '[name="SchedaBibliotecaTesto[extra' + prossimo + '_contenuto]"]' ).closest( 'tr' ).show();
aggiornaBottoneExtra();
}
} );
}
function initFonte() {
if ( !$( '[name="SchedaBiblioteca[titolo]"]' ).length ) return;
initGruppoFonte( 'continente', 4, 'Aggiungi continente' );
initGruppoFonte( 'area', 4, 'Aggiungi area' );
initGruppoFonte( 'paese', 4, 'Aggiungi paese' );
initGruppoFonte( 'macrosezione', 2, 'Aggiungi macrosezione' );
initGruppoFonte( 'sezione', 10, 'Aggiungi sezione' );
initGruppoFonte( 'sottosezione', 10, 'Aggiungi sottosezione' );
initGruppoFonte( 'tipologia', 8, 'Aggiungi tipologia' );
initGruppoFonte( 'sottotipologia',15, 'Aggiungi sottotipologia' );
initGruppoFonte( 'specificità', 15, 'Aggiungi specificità' );
initSezioniExtraFonte();
}
mw.hook( 'pf.formSetup' ).add( function () { initFonte(); } );
$( function () { initFonte(); } );
}( mw, jQuery ) );
/* Campi progressivi nella Maschera:Cinema */
( function ( mw, $ ) {
'use strict';
function getCampoCinema( prefisso, i, iniziaDaUno ) {
var nomeCampo = iniziaDaUno ? prefisso + i : ( i === 1 ? prefisso : prefisso + i );
var $campo = $( '[name="SchedaCinema[' + nomeCampo + ']"]' ).first();
if ( !$campo.length ) {
$campo = $( '[name="SchedaCinemaTesto[' + nomeCampo + ']"]' ).first();
}
return $campo;
}
function getRiga( $campo ) { return $campo.closest( 'tr' ); }
function setVisibile( $campo, visibile ) { getRiga( $campo ).toggle( visibile ); }
function isCompilato( $campo ) { var val = $campo.val(); return val !== '' && val !== null; }
function creaBottone( label ) {
return $( '<tr class="pf-aggiungi-row"><td colspan="2">' +
'<button type="button" class="pf-aggiungi-btn" ' +
'style="margin:2px 0 4px 0;padding:2px 10px;cursor:pointer;font-size:0.9em;">' +
'+ ' + label + '</button></td></tr>' );
}
function initGruppoCinema( prefisso, max, label, iniziaDaUno ) {
var campi = [];
for ( var i = 1; i <= max; i++ ) {
var $c = getCampoCinema( prefisso, i, iniziaDaUno );
if ( $c.length ) campi.push( $c );
}
if ( campi.length <= 1 ) return;
var $bottone = creaBottone( label || 'Aggiungi ' + prefisso );
var ultimoVisibile = 0;
for ( var j = 0; j < campi.length; j++ ) {
if ( isCompilato( campi[j] ) ) ultimoVisibile = j;
}
for ( var k = 0; k < campi.length; k++ ) {
setVisibile( campi[k], k <= ultimoVisibile );
}
function aggiornaBottone() {
$bottone.detach();
var ultimo = 0;
for ( var n = 0; n < campi.length; n++ ) {
if ( getRiga( campi[n] ).is( ':visible' ) ) ultimo = n;
}
if ( ultimo < campi.length - 1 && isCompilato( campi[ultimo] ) ) {
getRiga( campi[ultimo] ).after( $bottone );
}
}
aggiornaBottone();
$bottone.on( 'click', function () {
var ultimo = 0;
for ( var n = 0; n < campi.length; n++ ) {
if ( getRiga( campi[n] ).is( ':visible' ) ) ultimo = n;
}
if ( ultimo + 1 < campi.length ) {
setVisibile( campi[ultimo + 1], true );
aggiornaBottone();
}
} );
for ( var n = 0; n < campi.length; n++ ) {
( function ( idx ) {
campi[idx].on( 'input change', function () { aggiornaBottone(); } );
} )( n );
}
}
function initSezioniExtraCinema() {
for ( var i = 1; i <= 5; i++ ) {
var $titolo = $( '[name="SchedaCinemaTesto[extra' + i + '_titolo]"]' ).first();
var $contenuto = $( '[name="SchedaCinemaTesto[extra' + i + '_contenuto]"]' ).first();
if ( !$titolo.length ) continue;
var $rigaTitolo = $titolo.closest( 'tr' );
var $rigaContenuto = $contenuto.closest( 'tr' );
if ( i === 1 ) {
$rigaTitolo.show();
$rigaContenuto.show();
continue;
}
if ( !$titolo.val() ) {
$rigaTitolo.hide();
$rigaContenuto.hide();
}
}
var $bottone = $( '<tr><td colspan="2"><button type="button" ' +
'style="margin:2px 0 4px 0;padding:2px 10px;cursor:pointer;font-size:0.9em;">' +
'+ Aggiungi sezione</button></td></tr>' );
function aggiornaBottoneExtra() {
$bottone.detach();
var ultimoVisibile = 0;
for ( var n = 1; n <= 5; n++ ) {
var $t = $( '[name="SchedaCinemaTesto[extra' + n + '_titolo]"]' ).first();
if ( $t.length && $t.closest( 'tr' ).is( ':visible' ) ) ultimoVisibile = n;
}
if ( ultimoVisibile < 5 ) {
var $contenutoUltimo = $( '[name="SchedaCinemaTesto[extra' + ultimoVisibile + '_contenuto]"]' ).first();
$contenutoUltimo.closest( 'tr' ).after( $bottone );
}
}
aggiornaBottoneExtra();
$bottone.on( 'click', function () {
var prossimo = 0;
for ( var n = 1; n <= 5; n++ ) {
var $t = $( '[name="SchedaCinemaTesto[extra' + n + '_titolo]"]' ).first();
if ( $t.length && $t.closest( 'tr' ).is( ':hidden' ) ) { prossimo = n; break; }
}
if ( prossimo ) {
$( '[name="SchedaCinemaTesto[extra' + prossimo + '_titolo]"]' ).closest( 'tr' ).show();
$( '[name="SchedaCinemaTesto[extra' + prossimo + '_contenuto]"]' ).closest( 'tr' ).show();
aggiornaBottoneExtra();
}
} );
}
function initCinema() {
if ( !$( '[name="SchedaCinema[regista]"]' ).length ) return;
initGruppoCinema( 'continente', 4, 'Aggiungi continente' );
initGruppoCinema( 'area', 4, 'Aggiungi area' );
initGruppoCinema( 'paese', 4, 'Aggiungi paese' );
initGruppoCinema( 'macrosezione', 2, 'Aggiungi macrosezione' );
initGruppoCinema( 'sezione', 10, 'Aggiungi sezione' );
initGruppoCinema( 'sottosezione', 10, 'Aggiungi sottosezione' );
initGruppoCinema( 'tipologia', 8, 'Aggiungi tipologia' );
initGruppoCinema( 'sottotipologia',15, 'Aggiungi sottotipologia' );
initGruppoCinema( 'specificità', 15, 'Aggiungi specificità' );
initSezioniExtraCinema();
}
mw.hook( 'pf.formSetup' ).add( function () { initCinema(); } );
$( function () { initCinema(); } );
}( mw, jQuery ) );
/* Campi progressivi nella Maschera:Musica */
( function ( mw, $ ) {
'use strict';
function getCampoMusica( prefisso, i, iniziaDaUno ) {
var nomeCampo = iniziaDaUno ? prefisso + i : ( i === 1 ? prefisso : prefisso + i );
var $campo = $( '[name="SchedaMusica[' + nomeCampo + ']"]' ).first();
if ( !$campo.length ) {
$campo = $( '[name="SchedaMusicaTesto[' + nomeCampo + ']"]' ).first();
}
return $campo;
}
function getRiga( $campo ) { return $campo.closest( 'tr' ); }
function setVisibile( $campo, visibile ) { getRiga( $campo ).toggle( visibile ); }
function isCompilato( $campo ) { var val = $campo.val(); return val !== '' && val !== null; }
function creaBottone( label ) {
return $( '<tr class="pf-aggiungi-row"><td colspan="2">' +
'<button type="button" class="pf-aggiungi-btn" ' +
'style="margin:2px 0 4px 0;padding:2px 10px;cursor:pointer;font-size:0.9em;">' +
'+ ' + label + '</button></td></tr>' );
}
function initGruppoMusica( prefisso, max, label, iniziaDaUno ) {
var campi = [];
for ( var i = 1; i <= max; i++ ) {
var $c = getCampoMusica( prefisso, i, iniziaDaUno );
if ( $c.length ) campi.push( $c );
}
if ( campi.length <= 1 ) return;
var $bottone = creaBottone( label || 'Aggiungi ' + prefisso );
var ultimoVisibile = 0;
for ( var j = 0; j < campi.length; j++ ) {
if ( isCompilato( campi[j] ) ) ultimoVisibile = j;
}
for ( var k = 0; k < campi.length; k++ ) {
setVisibile( campi[k], k <= ultimoVisibile );
}
function aggiornaBottone() {
$bottone.detach();
var ultimo = 0;
for ( var n = 0; n < campi.length; n++ ) {
if ( getRiga( campi[n] ).is( ':visible' ) ) ultimo = n;
}
if ( ultimo < campi.length - 1 && isCompilato( campi[ultimo] ) ) {
getRiga( campi[ultimo] ).after( $bottone );
}
}
aggiornaBottone();
$bottone.on( 'click', function () {
var ultimo = 0;
for ( var n = 0; n < campi.length; n++ ) {
if ( getRiga( campi[n] ).is( ':visible' ) ) ultimo = n;
}
if ( ultimo + 1 < campi.length ) {
setVisibile( campi[ultimo + 1], true );
aggiornaBottone();
}
} );
for ( var n = 0; n < campi.length; n++ ) {
( function ( idx ) {
campi[idx].on( 'input change', function () { aggiornaBottone(); } );
} )( n );
}
}
function initSezioniExtraMusica() {
for ( var i = 1; i <= 5; i++ ) {
var $titolo = $( '[name="SchedaMusicaTesto[extra' + i + '_titolo]"]' ).first();
var $contenuto = $( '[name="SchedaMusicaTesto[extra' + i + '_contenuto]"]' ).first();
if ( !$titolo.length ) continue;
var $rigaTitolo = $titolo.closest( 'tr' );
var $rigaContenuto = $contenuto.closest( 'tr' );
if ( i === 1 ) {
$rigaTitolo.show();
$rigaContenuto.show();
continue;
}
if ( !$titolo.val() ) {
$rigaTitolo.hide();
$rigaContenuto.hide();
}
}
var $bottone = $( '<tr><td colspan="2"><button type="button" ' +
'style="margin:2px 0 4px 0;padding:2px 10px;cursor:pointer;font-size:0.9em;">' +
'+ Aggiungi sezione</button></td></tr>' );
function aggiornaBottoneExtra() {
$bottone.detach();
var ultimoVisibile = 0;
for ( var n = 1; n <= 5; n++ ) {
var $t = $( '[name="SchedaMusicaTesto[extra' + n + '_titolo]"]' ).first();
if ( $t.length && $t.closest( 'tr' ).is( ':visible' ) ) ultimoVisibile = n;
}
if ( ultimoVisibile < 5 ) {
var $contenutoUltimo = $( '[name="SchedaMusicaTesto[extra' + ultimoVisibile + '_contenuto]"]' ).first();
$contenutoUltimo.closest( 'tr' ).after( $bottone );
}
}
aggiornaBottoneExtra();
$bottone.on( 'click', function () {
var prossimo = 0;
for ( var n = 1; n <= 5; n++ ) {
var $t = $( '[name="SchedaMusicaTesto[extra' + n + '_titolo]"]' ).first();
if ( $t.length && $t.closest( 'tr' ).is( ':hidden' ) ) { prossimo = n; break; }
}
if ( prossimo ) {
$( '[name="SchedaMusicaTesto[extra' + prossimo + '_titolo]"]' ).closest( 'tr' ).show();
$( '[name="SchedaMusicaTesto[extra' + prossimo + '_contenuto]"]' ).closest( 'tr' ).show();
aggiornaBottoneExtra();
}
} );
}
function initMusica() {
if ( !$( '[name="SchedaMusica[album]"]' ).length ) return;
initGruppoMusica( 'continente', 4, 'Aggiungi continente' );
initGruppoMusica( 'area', 4, 'Aggiungi area' );
initGruppoMusica( 'paese', 4, 'Aggiungi paese' );
initGruppoMusica( 'macrosezione', 2, 'Aggiungi macrosezione' );
initGruppoMusica( 'sezione', 10, 'Aggiungi sezione' );
initGruppoMusica( 'sottosezione', 10, 'Aggiungi sottosezione' );
initGruppoMusica( 'tipologia', 8, 'Aggiungi tipologia' );
initGruppoMusica( 'sottotipologia',15, 'Aggiungi sottotipologia' );
initGruppoMusica( 'specificità', 15, 'Aggiungi specificità' );
initSezioniExtraMusica();
}
mw.hook( 'pf.formSetup' ).add( function () { initMusica(); } );
$( function () { initMusica(); } );
}( mw, jQuery ) );
/* Logiche condizionali cascade continente→area→paese (tutte le maschere) */
( function ( mw, $ ) {
'use strict';
var areePerContinente = {
'Africa': ['', 'Africa Centrale', 'Africa del Nord', 'Africa del Sud', 'Africa Occidentale', 'Africa Orientale'],
'Americhe': ['', 'Centroamerica', 'Nordamerica', 'Sudamerica'],
'Asia': ['', 'Asia Centrale', 'Asia Meridionale', 'Asia Settentrionale', 'Estremo Oriente', 'Medio Oriente', 'Sudest Asiatico'],
'Europa': ['', 'Europa Centrale', "Europa dell'Est", 'Europa Occidentale', 'Mediterraneo', 'Nordeuropa'],
'Oceania': ['', 'Antartico', 'Artico', 'Oceania Lontana', 'Oceania Vicina']
};
var paesiPerArea = {
'Africa del Nord': ['', 'Algeria', 'Egitto', 'Libia', 'Marocco', 'Sudan', 'Tunisia'],
'Africa Occidentale': ['', 'Benin', 'Burkina Faso', 'Camerun', "Costa d'Avorio", 'Gambia', 'Ghana', 'Guinea', 'Guinea Equatoriale', 'Guinea-Bissau', 'Liberia', 'Mali', 'Mauritania', 'Niger', 'Nigeria', 'Senegal', 'Sierra Leone', 'Togo'],
'Africa Centrale': ['', 'Angola', 'Burundi', 'Ciad', 'Congo', 'Gabon', 'Repubblica Centrafricana', 'Repubblica Democratica del Congo', 'Rwanda'],
'Africa Orientale': ['', 'Etiopia', 'Kenya', 'Madagascar', 'Malawi', 'Mozambico', 'Somalia', 'Sudan del Sud', 'Tanzania', 'Uganda'],
'Africa del Sud': ['', 'Botswana', 'Lesotho', 'Namibia', 'Sudafrica', 'Zambia', 'Zimbabwe'],
'Nordamerica': ['', 'Canada', 'Usa'],
'Centroamerica': ['', 'Antille', 'Belize', 'Costa Rica', 'Cuba', 'El Salvador', 'Giamaica', 'Guatemala', 'Haiti', 'Honduras', 'Messico', 'Nicaragua', 'Panama'],
'Sudamerica': ['', 'Argentina', 'Bolivia', 'Brasile', 'Cile', 'Colombia', 'Ecuador', 'Guyana', 'Paraguay', 'Perù', 'Suriname', 'Uruguay', 'Venezuela'],
'Asia Settentrionale': ['', 'Kazakhstan', 'Mongolia', 'Russia'],
'Asia Centrale': ['', 'Afghanistan', 'Kirghizistan', 'Tagikistan', 'Turkmenistan', 'Uzbekistan'],
'Medio Oriente': ['', 'Arabia Saudita', 'Bahrein', 'Emirati Arabi Uniti', 'Giordania', 'Iran', 'Iraq', 'Israele', 'Kuwait', 'Libano', 'Oman', 'Qatar', 'Siria', 'Turchia', 'Yemen'],
'Asia Meridionale': ['', 'Bangladesh', 'Bhutan', 'India', 'Maldive', 'Nepal', 'Pakistan', 'Sri Lanka'],
'Estremo Oriente': ['', 'Cina', 'Corea del Nord', 'Corea del Sud', 'Giappone', 'Mongolia', 'Tibet'],
'Sudest Asiatico': ['', 'Cambogia', 'Filippine', 'Indonesia', 'Laos', 'Malesia', 'Myanmar', 'Singapore', 'Thailandia', 'Timor Est', 'Vietnam'],
'Nordeuropa': ['', 'Danimarca', 'Fær Øer', 'Finlandia', 'Galles', 'Groenlandia', 'Inghilterra', 'Irlanda', 'Irlanda del Nord', 'Islanda', 'Norvegia', 'Scozia', 'Svezia'],
'Europa Centrale': ['', 'Austria', 'Belgio', 'Francia', 'Germania', 'Liechtenstein', 'Lussemburgo', 'Monaco', 'Olanda', 'Polonia', 'Repubblica Ceca', 'Slovacchia', 'Slovenia', 'Svizzera', 'Ungheria'],
'Europa Occidentale': ['', 'Andorra', 'Portogallo', 'Spagna'],
'Mediterraneo': ['', 'Albania', 'Bosnia ed Erzegovina', 'Cipro', 'Croazia', 'Grecia', 'Italia', 'Kosovo', 'Macedonia del Nord', 'Malta', 'Montenegro', 'Serbia', 'Slovenia'],
"Europa dell'Est": ['', 'Bielorussia', 'Bulgaria', 'Estonia', 'Lettonia', 'Lituania', 'Moldova', 'Romania', 'Russia', 'Serbia', 'Ucraina'],
'Oceania Vicina': ['', 'Australia', 'Isole Bismarck', 'Isole Salomone', 'Nuova Guinea', 'Nuova Zelanda', 'Papua Nuova Guinea'],
'Oceania Lontana': ['', 'Figi', 'Kiribati', 'Marshall', 'Micronesia', 'Nauru', 'Palau', 'Samoa', 'Tonga', 'Tuvalu', 'Vanuatu'],
'Artico': ['', 'Canada', 'Norvegia', 'Russia', 'Usa'],
'Antartico': ['']
};
function aggiornaSelect( $sel, opzioni ) {
if ( !$sel.length ) return;
var valAttuale = $sel.val();
$sel.empty();
$.each( opzioni, function ( i, v ) {
$sel.append( $( '<option>' ).val( v ).text( v ) );
} );
$sel.val( opzioni.indexOf( valAttuale ) !== -1 ? valAttuale : '' );
}
function sel( template, campo ) {
return $( '[name="' + template + '[' + campo + ']"]' );
}
/* Funzione generica: lega continente→area→paese per tutte le coppie di un template */
function initCondizionaliGenerica( template, coppie ) {
/* Sentinel: esce se il form non è presente nella pagina */
if ( !sel( template, coppie[0].paese ).length ) return;
$.each( coppie, function ( i, c ) {
if ( !c.cont || !c.area ) return; /* Salta coppie senza continente/area */
var $cont = sel( template, c.cont );
var $area = sel( template, c.area );
var $paese = sel( template, c.paese );
/* Inizializzazione per pagine in modifica */
var valCont = $cont.val();
if ( valCont && areePerContinente[ valCont ] ) {
aggiornaSelect( $area, areePerContinente[ valCont ] );
}
var valArea = $area.val();
if ( valArea && paesiPerArea[ valArea ] ) {
aggiornaSelect( $paese, paesiPerArea[ valArea ] );
}
/* Continente → Area */
$cont.on( 'change', function () {
var val = $( this ).val();
aggiornaSelect( $area, areePerContinente[ val ] || [ '' ] );
aggiornaSelect( $paese, [ '' ] );
} );
/* Area → Paese */
$area.on( 'change', function () {
var val = $( this ).val();
aggiornaSelect( $paese, paesiPerArea[ val ] || [ '' ] );
} );
} );
}
var MASCHERE = [
{ template: 'Racconto', coppie: [
{ cont: 'continente', area: 'area', paese: 'paese' },
{ cont: 'continente2', area: 'area2', paese: 'paese2' },
{ cont: 'continente3', area: 'area3', paese: 'paese3' }
] },
{ template: 'SchedaCinema', coppie: [
{ cont: 'continente', area: 'area', paese: 'paese' },
{ cont: 'continente2', area: 'area2', paese: 'paese2' },
{ cont: 'continente3', area: 'area3', paese: 'paese3' },
{ cont: 'continente4', area: 'area4', paese: 'paese4' }
] },
{ template: 'SchedaMusica', coppie: [
{ cont: 'continente', area: 'area', paese: 'paese' },
{ cont: 'continente2', area: 'area2', paese: 'paese2' },
{ cont: 'continente3', area: 'area3', paese: 'paese3' },
{ cont: 'continente4', area: 'area4', paese: 'paese4' }
] },
{ template: 'SchedaLetteratura', coppie: [
{ cont: 'continente', area: 'area', paese: 'paese' },
{ cont: 'continente2', area: 'area2', paese: 'paese2' },
{ cont: 'continente3', area: 'area3', paese: 'paese3' },
{ cont: 'continente4', area: 'area4', paese: 'paese4' }
] },
{ template: 'SchedaBiblioteca', coppie: [
{ cont: 'continente', area: 'area', paese: 'paese' },
{ cont: 'continente2', area: 'area2', paese: 'paese2' },
{ cont: 'continente3', area: 'area3', paese: 'paese3' },
{ cont: 'continente4', area: 'area4', paese: 'paese4' }
] }
];
function init() {
$.each( MASCHERE, function ( i, m ) {
initCondizionaliGenerica( m.template, m.coppie );
} );
}
mw.hook( 'pf.formSetup' ).add( function () { init(); } );
$( function () { init(); } );
}( mw, jQuery ) );
mw.hook('wikipage.content').add(function() {
var banner = document.querySelector('#custom-banner');
if (banner) {
var div = document.createElement('div');
div.style.cssText = 'text-align:left; padding: 8px 16px; display:flex; align-items:center; gap:8px;';
div.innerHTML = '<b style="font-size: 13px;">Sostieni Il Crepuscolo degli Dèi con una</b>' +
'<form action="https://www.paypal.com/donate" method="post" target="_top" style="display:inline;">' +
'<input type="hidden" name="business" value="3XAXCCK693NDE" />' +
'<input type="hidden" name="no_recurring" value="0" />' +
'<input type="hidden" name="item_name" value="Sostieni Il Crepuscolo degli Dei e aiutaci a migliorarne costantemente i contenuti." />' +
'<input type="hidden" name="currency_code" value="EUR" />' +
'<input type="image" src="https://www.paypalobjects.com/it_IT/IT/i/btn/btn_donate_SM.gif" border="0" name="submit" alt="Fai una donazione con il pulsante PayPal" />' +
'<img alt="" border="0" src="https://www.paypal.com/it_IT/i/scr/pixel.gif" width="1" height="1" />' +
'</form>';
banner.parentNode.insertBefore(div, banner.nextSibling);
}
});
/* =========================================================
IMAGE PICKER — Selettore immagini per tutte le maschere
========================================================= */
( function ( mw, $ ) {
'use strict';
var API = 'https://www.ilcrepuscolodeglidei.org/api.php';
function creaModal() {
if ( $( '#image-picker-modal' ).length ) return;
var $overlay = $( '<div id="image-picker-modal"></div>' ).css( {
display: 'none', position: 'fixed', top: 0, left: 0,
width: '100%', height: '100%', background: 'rgba(0,0,0,0.7)',
'z-index': 99999, 'overflow-y': 'auto'
} );
var $box = $( '<div></div>' ).css( {
background: '#fff', margin: '40px auto', 'max-width': '900px',
'border-radius': '8px', padding: '20px', position: 'relative'
} );
var $closeBtn = $( '<button type="button">✕</button>' ).css( {
position: 'absolute', top: '10px', right: '10px',
'font-size': '20px', background: 'none', border: 'none', cursor: 'pointer'
} ).attr( 'id', 'image-picker-close' );
var $title = $( '<h3>Seleziona immagine</h3>' ).css( { margin: '0 0 15px', 'font-size': '1.1em' } );
var $searchRow = $( '<div></div>' ).css( { display: 'flex', gap: '8px', 'margin-bottom': '15px' } );
var $searchInput = $( '<input type="text" placeholder="Cerca per nome file...">' )
.attr( 'id', 'image-picker-search' )
.css( { flex: 1, padding: '8px', 'font-size': '14px', border: '1px solid #ccc', 'border-radius': '4px' } );
var $uploadBtn = $( '<button type="button">📁 Carica nuovo</button>' )
.attr( 'id', 'image-picker-upload' )
.css( { padding: '8px 14px', background: '#333', color: '#fff', border: 'none', 'border-radius': '4px', cursor: 'pointer', 'font-size': '13px' } );
$searchRow.append( $searchInput, $uploadBtn );
var $status = $( '<div></div>' ).attr( 'id', 'image-picker-status' )
.css( { 'font-size': '13px', color: '#666', 'margin-bottom': '10px' } );
var $gallery = $( '<div></div>' ).attr( 'id', 'image-picker-gallery' ).css( {
display: 'grid',
'grid-template-columns': 'repeat(auto-fill,minmax(150px,1fr))',
gap: '10px', 'max-height': '500px', 'overflow-y': 'auto'
} );
$box.append( $closeBtn, $title, $searchRow, $status, $gallery );
$overlay.append( $box );
$( 'body' ).append( $overlay );
$( '#image-picker-close' ).on( 'click', function () {
$overlay.hide();
} );
$overlay.on( 'click', function ( e ) {
if ( $( e.target ).is( $overlay ) ) $overlay.hide();
} );
$( '#image-picker-upload' ).on( 'click', function () {
var popup = window.open(
mw.util.getUrl( 'Speciale:Carica' ),
'upload_popup',
'width=800,height=600,scrollbars=yes,resizable=yes'
);
var checker = setInterval( function () {
if ( !popup || popup.closed ) {
clearInterval( checker );
cercaImmagini( $( '#image-picker-search' ).val() );
return;
}
try {
var url = popup.location.href;
var match = url.match( /(?:File|Immagine|Image):([^&?#]+)/i );
if ( match ) {
clearInterval( checker );
popup.close();
cercaImmagini( '' );
}
} catch ( e ) {}
}, 500 );
} );
var searchTimer;
$( '#image-picker-search' ).on( 'input', function () {
clearTimeout( searchTimer );
var q = $( this ).val();
searchTimer = setTimeout( function () { cercaImmagini( q ); }, 300 );
} );
}
var currentInput = null;
function apriPicker( $input ) {
currentInput = $input;
creaModal();
$( '#image-picker-modal' ).show();
$( '#image-picker-search' ).val( '' ).focus();
$( '#image-picker-gallery' ).empty();
$( '#image-picker-status' ).text( 'Caricamento immagini...' );
cercaImmagini( '' );
}
function cercaImmagini( query ) {
$( '#image-picker-status' ).text( 'Ricerca in corso...' );
$( '#image-picker-gallery' ).empty();
var params = {
action: 'query',
list: 'allimages',
ailimit: 50,
aiprop: 'url|size',
aisort: 'name',
format: 'json'
};
if ( query ) {
params.aiprefix = query;
}
$.getJSON( API, params, function ( data ) {
var immagini = data.query && data.query.allimages || [];
if ( query ) {
immagini = immagini.filter( function ( img ) {
return img.name.toLowerCase().indexOf( query.toLowerCase() ) !== -1;
} );
}
if ( immagini.length === 0 ) {
$( '#image-picker-status' ).text( 'Nessuna immagine trovata.' );
return;
}
$( '#image-picker-status' ).text( immagini.length + ' immagini trovate' );
$.each( immagini, function ( i, img ) {
var $item = $( '<div></div>' ).css( {
border: '2px solid #eee', 'border-radius': '4px',
padding: '5px', cursor: 'pointer', 'text-align': 'center'
} ).data( 'name', img.name );
var $imgWrap = $( '<div></div>' ).css( {
height: '120px', display: 'flex',
'align-items': 'center', 'justify-content': 'center', overflow: 'hidden'
} );
var $img = $( '<img>' ).attr( 'src', img.url ).css( {
'max-width': '100%', 'max-height': '120px', 'object-fit': 'contain'
} );
var $label = $( '<div></div>' ).text( img.name ).css( {
'font-size': '11px', 'margin-top': '4px', 'word-break': 'break-all',
color: '#444', 'white-space': 'nowrap', overflow: 'hidden', 'text-overflow': 'ellipsis'
} ).attr( 'title', img.name );
$imgWrap.append( $img );
$item.append( $imgWrap, $label );
$item.on( 'mouseenter', function () {
$( this ).css( 'border-color', '#4a90d9' );
} ).on( 'mouseleave', function () {
$( this ).css( 'border-color', '#eee' );
} ).on( 'click', function () {
var nome = $( this ).data( 'name' );
if ( currentInput ) {
currentInput.val( '[[Immagine:' + nome + '|200px]]' );
}
$( '#image-picker-modal' ).hide();
} );
$( '#image-picker-gallery' ).append( $item );
} );
caricaThumbnail( immagini );
} ).fail( function () {
$( '#image-picker-status' ).text( 'Errore durante la ricerca.' );
} );
}
function caricaThumbnail( immagini ) {
var titoli = immagini.slice( 0, 50 ).map( function ( img ) {
return 'File:' + img.name;
} );
if ( titoli.length === 0 ) return;
$.getJSON( API, {
action: 'query',
titles: titoli.join( '|' ),
prop: 'imageinfo',
iiprop: 'url',
iiurlwidth: 150,
format: 'json'
}, function ( data ) {
var pages = data.query && data.query.pages || {};
$.each( pages, function ( id, page ) {
if ( page.imageinfo && page.imageinfo[0] ) {
var thumbUrl = page.imageinfo[0].thumburl;
var nome = page.title.replace( /^(?:File|Immagine):/, '' );
$( '#image-picker-gallery' ).find( 'div' ).filter( function () {
return $( this ).data( 'name' ) === nome;
} ).find( 'img' ).attr( 'src', thumbUrl );
}
} );
} );
}
function initImagePicker() {
$( 'input[name$="[immagine]"]' ).each( function () {
var $input = $( this );
if ( $input.data( 'picker-init' ) ) return;
$input.data( 'picker-init', true );
var $btnPicker = $( '<button type="button">🖼 Scegli</button>' ).css( {
'margin-left': '6px', padding: '2px 10px', cursor: 'pointer', 'font-size': '0.85em'
} );
var $btnUpload = $( '<button type="button">📁 Carica nel Museo</button>' ).css( {
'margin-left': '4px', padding: '2px 10px', cursor: 'pointer', 'font-size': '0.85em'
} );
var $btnCaricaFile = $( '<button type="button">⬆ Carica file</button>' ).css( {
'margin-left': '4px', padding: '2px 10px', cursor: 'pointer', 'font-size': '0.85em'
} );
$input.after( $btnCaricaFile ).after( $btnUpload ).after( $btnPicker );
$btnPicker.on( 'click', function () {
apriPicker( $input );
} );
$btnUpload.on( 'click', function () {
window.apriMuseoUploader( function( nomeFile ) {
$input.val( '[[Immagine:' + nomeFile + '|200px]]' );
} );
} );
$btnCaricaFile.on( 'click', function () {
var popup = window.open(
mw.util.getUrl( 'Speciale:Carica' ),
'upload_popup',
'width=800,height=600,scrollbars=yes,resizable=yes'
);
var checker = setInterval( function () {
if ( !popup || popup.closed ) { clearInterval( checker ); return; }
try {
var url = popup.location.href;
var match = url.match( /(?:File|Immagine|Image):([^&?#]+)/i );
if ( match ) {
var nomeFile = decodeURIComponent( match[1].replace( /_/g, ' ' ) );
$input.val( '[[Immagine:' + nomeFile + '|200px]]' );
popup.close();
clearInterval( checker );
}
} catch ( e ) {}
}, 500 );
} );
} );
}
mw.hook( 'pf.formSetup' ).add( function () { setTimeout( initImagePicker, 300 ); } );
$( function () { setTimeout( initImagePicker, 300 ); } );
}( mw, jQuery ) );
/* =========================================================
MUSEO UPLOADER — Form caricamento immagini con template Museo
Funziona sia come popup (dall'Image Picker) sia come standalone
========================================================= */
( function ( mw, $ ) {
'use strict';
var API = mw.util.wikiScript( 'api' );
var TIPOLOGIE = [ '', 'Ceramica', 'Disegno', 'Pittura', 'Scultura' ];
var TECNICHE = [
'', 'Acquarello su carta', 'Acquarello su tela', 'Acquerello su tavola',
'Acrilico su pannello', 'Acrilico su tela', 'Affresco', 'Altorilievo',
'Bassorilievo', 'Calcare', 'Cammeo', 'Carboncino', 'Crayon',
'Figure Nere', 'Figure Rosse', 'Gessetto', 'Gesso', 'Gruppo scultoreo',
'Guazzo', 'Inchiostro', 'Incisione', 'Intarsio', 'Litografia',
'Maioliche', 'Matita e acquerelli', 'Matita su carta', 'Mosaico',
'Olio e tempera su cartone', 'Olio su alabastro', 'Olio su carta',
'Olio su intonaco', 'Olio su pannello', 'Olio su rame', 'Olio su tavola',
'Olio su tela', 'Pastelli', 'Penna e inchiostro',
'Sanguigna e grafite su carta', 'Statua', 'Statua in bronzo',
'Statua in legno', 'Statua in marmo', 'Statua in pietra',
'Statua in stucco', 'Statua in terracotta', 'Tempera su carta',
'Tempera su pannello', 'Tempera su tela', 'Vetrata artistica', 'Xilografia'
];
var PERIODI = [ '', 'Arte Antica', 'Arte Contemporanea', 'Arte Medievale', 'Arte Moderna' ];
var STILI = [
'', 'Accademico', 'Arcaico', 'Art Déco', 'Barocco', 'Bizantino', 'Classico',
'Egizio', 'Ellenistico', 'Golden Age', 'Gotico', 'Imperiale', 'Impressionista',
'Liberty', 'Manierista', 'Neoclassico', 'Preraffaellita', 'Preraffaelliti',
'Realista', 'Repubblicano', 'Rinascimentale', 'Rococò', 'Romantico',
'Simbolista', 'Vittoriano'
];
var MACROZONE = [ '', 'Mitologia', 'Folklore' ];
var SEZIONI_PER_MACRO = {
'Mitologia': [
'', 'Altre Mitologie', 'Mitologia Africana Tribale', 'Mitologia Afroamericana',
'Mitologia Artica', 'Mitologia Asiatica Tribale', 'Mitologia Baltica',
'Mitologia Basca', 'Mitologia Celtica', 'Mitologia Classica',
'Mitologia Egizia', 'Mitologia Germanica', 'Mitologia Indo-Iranica',
'Mitologia Islamica', 'Mitologia Medievale', 'Mitologia Mesopotamica',
'Mitologia Oceanica', 'Mitologia Orientale', 'Mitologia Pellerossa',
'Mitologia Precolombiana', 'Mitologia Semitica', 'Mitologia Slava',
'Mitologia Ugro-Finnica'
],
'Folklore': [ '', 'Itinerari Arcani', 'Misteri', 'Simboli' ]
};
var SOTTOSEZIONI_PER_SEZIONE = {
'Mitologia Africana Tribale': [ '', 'Mitologia Akamba', 'Mitologia Akan', 'Mitologia Ashanti', 'Mitologia Bakongo', 'Mitologia Bambara', 'Mitologia Cabila', 'Mitologia Dinka', 'Mitologia Dogon', 'Mitologia Efik', 'Mitologia Ewe', 'Mitologia Fon', 'Mitologia Khoi', 'Mitologia Khoisan', 'Mitologia Kikuyu', 'Mitologia Lotuko', 'Mitologia Lugbara', 'Mitologia Malgascia', 'Mitologia Ngombe', 'Mitologia Odinani', 'Mitologia Ovambo', 'Mitologia Peul', 'Mitologia San', 'Mitologia Yoruba', 'Mitologia Zulu' ],
'Mitologia Afroamericana': [ '', 'Mitologia Candomblé', 'Mitologia Santera', 'Mitologia Voodoo' ],
'Mitologia Artica': [ '', 'Mitologia Ainu', 'Mitologia Inuit', 'Mitologia Lappone' ],
'Mitologia Asiatica Tribale': [ '', 'Mitologia Filippina', 'Mitologia Indonesiana', 'Mitologia Kazaka', 'Mitologia Malese', 'Mitologia Tibetana', 'Mitologia Turco-Mongola' ],
'Mitologia Baltica': [ '', 'Mitologia Lettone', 'Mitologia Lituana' ],
'Mitologia Celtica': [ '', 'Mitologia Britannica', 'Mitologia Gallese', 'Mitologia Gallica', 'Mitologia Irlandese' ],
'Mitologia Classica': [ '', 'Mitologia Etrusca', 'Mitologia Greca', 'Mitologia Italica', 'Mitologia Romana' ],
'Mitologia Germanica': [ '', 'Mitologia Germanica Continentale', 'Mitologia Nibelungica', 'Mitologia Norrena' ],
'Mitologia Indo-Iranica': [ '', 'Mitologia Indiana', 'Mitologia Iranica', 'Mitologia Osseta', 'Mitologia Tamil' ],
'Mitologia Medievale': [ '', 'Mitologia Arturiana', 'Mitologia Carolingia', 'Mitologia Cristiana' ],
'Mitologia Mesopotamica': [ '', 'Mitologia Babilonese', 'Mitologia Ittita', 'Mitologia Sumera', 'Mitologia Urrita' ],
'Mitologia Oceanica': [ '', 'Mitologia Aborigena', 'Mitologia Maori', 'Mitologia Melanesiana', 'Mitologia Micronesiana', 'Mitologia Polinesiana' ],
'Mitologia Orientale': [ '', 'Mitologia Buddhista', 'Mitologia Cinese', 'Mitologia Coreana', 'Mitologia Giainista', 'Mitologia Giapponese', 'Mitologia Taoista' ],
'Mitologia Pellerossa': [ '', 'Mitologia Acoma', 'Mitologia Algonchina', 'Mitologia Alsea', 'Mitologia Arapaho', 'Mitologia Arikara', 'Mitologia Bella Coola', 'Mitologia Bilquala', 'Mitologia Caddo', 'Mitologia Cherokee', 'Mitologia Cheyenne', 'Mitologia Chickasaw', 'Mitologia Chinook', 'Mitologia Chipewyan', 'Mitologia Chiwere', 'Mitologia Choctaw', 'Mitologia Creek', 'Mitologia Crow', 'Mitologia Dakota', 'Mitologia Diegueño', 'Mitologia Haida', 'Mitologia Hokan', 'Mitologia Hopi', 'Mitologia Iowa', 'Mitologia Irochese', 'Mitologia Keres', 'Mitologia Kiowa', 'Mitologia Kwa', 'Mitologia Kwakiutl', 'Mitologia Lakota', 'Mitologia Lenape', 'Mitologia Lillooet', 'Mitologia Maidu', 'Mitologia Maliseet', 'Mitologia Micmac', 'Mitologia Miskito', 'Mitologia Mohave', 'Mitologia Mohawk', 'Mitologia Montagnais', 'Mitologia Muskogi', 'Mitologia Nadene', 'Mitologia Natchez', 'Mitologia Navaho', 'Mitologia Nootka', 'Mitologia Oglala', 'Mitologia Ojibwa', 'Mitologia Okanagan', 'Mitologia Onondaga', 'Mitologia Passamaquoddy', 'Mitologia Pawnee', 'Mitologia Penuti', 'Mitologia Pericue', 'Mitologia Piedi Neri', 'Mitologia Pima', 'Mitologia Pomo', 'Mitologia Ponca', 'Mitologia Powhatan', 'Mitologia Pueblo', 'Mitologia Puget', 'Mitologia Salish', 'Mitologia Sauk', 'Mitologia Seminole', 'Mitologia Seneca', 'Mitologia Sioux', 'Mitologia Snohomish', 'Mitologia Tanaina', 'Mitologia Tewa', 'Mitologia Tiwa', 'Mitologia Tlingit', 'Mitologia Tongva', 'Mitologia Tsimshian', 'Mitologia Uroni', 'Mitologia Wakashan', 'Mitologia Washoe', 'Mitologia Wichita', 'Mitologia Winnebago', 'Mitologia Wintun', 'Mitologia Wiyot', 'Mitologia Yavapai', 'Mitologia Yuchi', 'Mitologia Yuki', 'Mitologia Zuni' ],
'Mitologia Precolombiana': [ '', 'Mitologia Amazzonica', 'Mitologia Azteca', 'Mitologia Chamacoco', 'Mitologia Guarani', 'Mitologia Inca', 'Mitologia Maya', 'Mitologia Taino' ],
'Mitologia Semitica': [ '', 'Mitologia Araba', 'Mitologia Ebraica', 'Mitologia Fenicia', 'Mitologia Gnostica' ],
'Mitologia Slava': [ '', 'Mitologia Slava Meridionale', 'Mitologia Slava Occidentale', 'Mitologia Slava Orientale' ],
'Mitologia Ugro-Finnica': [ '', 'Mitologia Estone', 'Mitologia Finnica', 'Mitologia Magiara' ]
};
// Cache dati esterni
var _autori = [];
var _ubicazioni = [];
var _voci = [];
var _datiCaricati = false;
function caricaDatiEsterni( callback ) {
if ( _datiCaricati ) { callback(); return; }
var pending = 3;
function done() { pending--; if ( pending === 0 ) { _datiCaricati = true; callback(); } }
// Autori
$.getJSON( API, { action: 'query', titles: 'MediaWiki:MuseoAutori.json', prop: 'revisions', rvprop: 'content', format: 'json' }, function( data ) {
try {
var pages = data.query.pages;
var content = pages[ Object.keys( pages )[0] ].revisions[0]['*'];
_autori = JSON.parse( content ).autori || [];
} catch(e) { _autori = []; }
done();
} ).fail( done );
// Ubicazioni
$.getJSON( API, { action: 'query', titles: 'MediaWiki:MuseoUbicazioni.json', prop: 'revisions', rvprop: 'content', format: 'json' }, function( data ) {
try {
var pages = data.query.pages;
var content = pages[ Object.keys( pages )[0] ].revisions[0]['*'];
_ubicazioni = JSON.parse( content ).ubicazioni || [];
} catch(e) { _ubicazioni = []; }
done();
} ).fail( done );
// Voci dalla categoria — con paginazione completa
function caricaPaginaVoci( continua, accumulato ) {
var params = { action: 'query', list: 'categorymembers', cmtitle: 'Categoria:Voci', cmlimit: 500, format: 'json' };
if ( continua ) params.cmcontinue = continua;
$.getJSON( API, params, function( data ) {
var nuove = ( data.query && data.query.categorymembers || [] ).map( function(p) { return p.title; } );
accumulato = accumulato.concat( nuove );
if ( data.continue && data.continue.cmcontinue ) {
caricaPaginaVoci( data.continue.cmcontinue, accumulato );
} else {
_voci = accumulato;
done();
}
} ).fail( function() { _voci = accumulato; done(); } );
}
caricaPaginaVoci( null, [] );
}
var CONTINENTI = [ '', 'Africa', 'Americhe', 'Asia', 'Europa', 'Oceania', 'Poli' ];
var AREE_PER_CONTINENTE = {
'Africa': [ '', 'Africa del Nord', 'Africa Occidentale', 'Africa Centrale', 'Africa Orientale', 'Africa del Sud' ],
'Americhe': [ '', 'Nordamerica', 'Centroamerica', 'Sudamerica' ],
'Asia': [ '', 'Asia Settentrionale', 'Asia Centrale', 'Asia Meridionale', 'Medio Oriente', 'Estremo Oriente', 'Sudest Asiatico' ],
'Europa': [ '', 'Nordeuropa', "Europa dell'Est", 'Europa Centrale', 'Europa Occidentale', 'Mediterraneo' ],
'Oceania': [ '', 'Oceania Vicina', 'Oceania Lontana' ],
'Poli': [ '', 'Artico', 'Antartico' ]
};
var PAESI_PER_AREA = {
'Africa del Nord': [ '', 'Egitto', 'Marocco' ],
'Africa Occidentale': [ '', 'Nigeria', 'Ghana' ],
'Africa Centrale': [ '', 'Congo', 'Camerun' ],
'Africa Orientale': [ '', 'Kenya', 'Etiopia' ],
'Africa del Sud': [ '', 'Sudafrica', 'Zimbabwe' ],
'Nordamerica': [ '', 'Usa', 'Canada' ],
'Centroamerica': [ '', 'Messico', 'Guatemala' ],
'Sudamerica': [ '', 'Brasile', 'Perù' ],
'Asia Settentrionale': [ '', 'Siberia', 'Mongolia' ],
'Asia Centrale': [ '', 'Kazakhstan', 'Uzbekistan' ],
'Asia Meridionale': [ '', 'India', 'Nepal' ],
'Medio Oriente': [ '', 'Iraq', 'Iran' ],
'Estremo Oriente': [ '', 'Cina', 'Giappone' ],
'Sudest Asiatico': [ '', 'Thailandia', 'Indonesia' ],
'Nordeuropa': [ '', 'Danimarca', 'Finlandia', 'Galles', 'Inghilterra', 'Norvegia', 'Scozia', 'Svezia' ],
"Europa dell'Est": [ '', 'Russia', 'Polonia' ],
'Europa Centrale': [ '', 'Germania', 'Austria' ],
'Europa Occidentale': [ '', 'Spagna', 'Francia' ],
'Mediterraneo': [ '', 'Grecia', 'Italia' ],
'Oceania Vicina': [ '', 'Australia', 'Nuova Zelanda' ],
'Oceania Lontana': [ '', 'Hawaii', 'Polinesia' ],
'Artico': [ '', 'Groenlandia', 'Alaska' ],
'Antartico': [ '', 'Antartide', 'Isole Sub-Antartiche' ]
};
function creaSelect( opzioni, id, cls ) {
var $sel = $( '<select>' ).attr( 'id', id ).addClass( cls || 'museo-select' ).css( { width: '100%', padding: '6px', 'border-radius': '4px', border: '1px solid #ccc', 'font-size': '13px' } );
$.each( opzioni, function( i, v ) { $sel.append( $( '<option>' ).val( v ).text( v ) ); } );
return $sel;
}
function creaCombobox( suggerimenti, id, placeholder ) {
var $wrap = $( '<div>' ).css( { position: 'relative' } );
var $input = $( '<input type="text">' ).attr( { id: id, placeholder: placeholder || '', autocomplete: 'off' } ).css( { width: '100%', padding: '6px', 'border-radius': '4px', border: '1px solid #ccc', 'font-size': '13px', 'box-sizing': 'border-box' } );
var $lista = $( '<div>' ).css( { position: 'absolute', top: '100%', left: 0, right: 0, background: '#fff', border: '1px solid #ccc', 'border-top': 'none', 'max-height': '180px', 'overflow-y': 'auto', 'z-index': 9999, display: 'none', 'border-radius': '0 0 4px 4px' } );
$input.on( 'input', function() {
var q = $( this ).val().toLowerCase();
$lista.empty();
if ( !q ) { $lista.hide(); return; }
var matches = suggerimenti.filter( function(s) { return s.toLowerCase().indexOf( q ) !== -1; } ).slice( 0, 20 );
if ( !matches.length ) { $lista.hide(); return; }
$.each( matches, function( i, s ) {
$( '<div>' ).text( s ).css( { padding: '6px 10px', cursor: 'pointer', 'font-size': '13px' } )
.on( 'mouseenter', function() { $( this ).css( 'background', '#f0f4ff' ); } )
.on( 'mouseleave', function() { $( this ).css( 'background', '#fff' ); } )
.on( 'mousedown', function() { $input.val( s ); $lista.hide(); } )
.appendTo( $lista );
} );
$lista.show();
} ).on( 'blur', function() { setTimeout( function() { $lista.hide(); }, 150 ); } );
$wrap.append( $input, $lista );
return $wrap;
}
function aggiornaSelect( $sel, opzioni ) {
var val = $sel.val();
$sel.empty();
$.each( opzioni, function( i, v ) { $sel.append( $( '<option>' ).val( v ).text( v ) ); } );
if ( opzioni.indexOf( val ) !== -1 ) $sel.val( val );
}
function costruisciTemplate( dati ) {
var tags = dati.tags.filter( Boolean ).map( function(t) { return '[[' + t + ']]'; } ).join( ', ' );
var miniatura = dati.nomeFile ? '[[Immagine:' + dati.nomeFile + '|100px]]' : '';
var tpl = '{{Museo\n' +
'|anagrafica=SCHEDA\n' +
'|autore=' + dati.autore + '\n' +
'|continente=' + dati.continente + '\n' +
'|area=' + dati.area + '\n' +
'|paese=' + dati.paese + '\n' +
'|titolo=' + dati.titolo + '\n' +
'|tipologia=' + dati.tipologia + '\n' +
'|tecnica=' + dati.tecnica + '\n' +
'|periodo=' + dati.periodo + '\n' +
'|datazione=' + dati.datazione + '\n' +
'|ubicazione=' + dati.ubicazione + '\n' +
'|stile=' + dati.stile + '\n' +
'|descrizione=' + dati.descrizione + '\n' +
'|macrosezione=' + dati.macroSezioni[0] + '\n' +
'|macrosezione2=' + dati.macroSezioni[1] + '\n' +
'|sezione=' + dati.sezioni[0] + '\n' +
'|sezione2=' + dati.sezioni[1] + '\n' +
'|sezione3=' + dati.sezioni[2] + '\n' +
'|sezione4=' + dati.sezioni[3] + '\n' +
'|sottosezione=' + dati.sottoSezioni[0] + '\n' +
'|sottosezione2=' + dati.sottoSezioni[1] + '\n' +
'|sottosezione3=' + dati.sottoSezioni[2] + '\n' +
'|sottosezione4=' + dati.sottoSezioni[3] + '\n' +
'|tag=' + tags + '\n' +
'|miniatura=' + miniatura + '\n' +
'}}\n' +
'{{Navigazione (museo)}}\n' +
'[[Categoria:Museo]]';
return tpl;
}
function creaForm( $container, onSuccess ) {
$container.empty();
var $form = $( '<div>' ).css( { 'font-family': 'sans-serif', 'font-size': '13px', padding: '10px' } );
// Stile righe
function riga( label, $campo ) {
var $r = $( '<div>' ).css( { display: 'grid', 'grid-template-columns': '130px 1fr', gap: '8px', 'align-items': 'center', 'margin-bottom': '10px' } );
$r.append( $( '<label>' ).text( label ).css( { 'font-weight': 'bold', 'font-size': '13px' } ), $campo );
return $r;
}
// File
var $fileInput = $( '<input type="file" accept="image/*">' ).css( { width: '100%', 'font-size': '13px' } );
var $anteprima = $( '<img>' ).css( { 'max-width': '200px', 'max-height': '150px', display: 'none', 'margin-top': '6px', 'border-radius': '4px', border: '1px solid #ddd' } );
var $nomeFileLabel = $( '<div>' ).css( { 'font-size': '12px', color: '#666', 'margin-top': '4px' } );
var nomeFileCorrente = '';
$fileInput.on( 'change', function() {
var file = this.files[0];
if ( !file ) return;
nomeFileCorrente = file.name;
$nomeFileLabel.text( nomeFileCorrente );
var reader = new FileReader();
reader.onload = function(e) { $anteprima.attr( 'src', e.target.result ).show(); };
reader.readAsDataURL( file );
} );
var $fileWrap = $( '<div>' );
$fileWrap.append( $fileInput, $nomeFileLabel, $anteprima );
$form.append( riga( 'File:', $fileWrap ) );
// Titolo
var $titolo = $( '<input type="text">' ).css( { width: '100%', padding: '6px', 'border-radius': '4px', border: '1px solid #ccc', 'font-size': '13px', 'box-sizing': 'border-box' } );
$form.append( riga( 'Titolo:', $titolo ) );
// Autore combobox
var $autoreWrap = creaCombobox( _autori, 'museo-autore', 'Cerca o inserisci autore...' );
$form.append( riga( 'Autore:', $autoreWrap ) );
// Continente
var $continente = creaSelect( CONTINENTI, 'museo-continente' );
$form.append( riga( 'Continente:', $continente ) );
// Area
var $area = creaSelect( [''], 'museo-area' );
$form.append( riga( 'Area:', $area ) );
// Paese
var $paese = creaSelect( [''], 'museo-paese' );
$form.append( riga( 'Paese:', $paese ) );
// Logiche cascata geografica
$continente.on( 'change', function() {
var val = $( this ).val();
aggiornaSelect( $area, AREE_PER_CONTINENTE[ val ] || [''] );
aggiornaSelect( $paese, [''] );
} );
$area.on( 'change', function() {
var val = $( this ).val();
aggiornaSelect( $paese, PAESI_PER_AREA[ val ] || [''] );
} );
// Tipologia
var $tipologia = creaSelect( TIPOLOGIE, 'museo-tipologia' );
$form.append( riga( 'Tipologia:', $tipologia ) );
// Tecnica
var $tecnica = creaSelect( TECNICHE, 'museo-tecnica' );
$form.append( riga( 'Tecnica:', $tecnica ) );
// Periodo
var $periodo = creaSelect( PERIODI, 'museo-periodo' );
$form.append( riga( 'Periodo:', $periodo ) );
// Stile combobox
var $stileWrap = creaCombobox( STILI, 'museo-stile', 'Cerca o inserisci stile...' );
$form.append( riga( 'Stile:', $stileWrap ) );
// Datazione
var $datazione = $( '<input type="text" placeholder="es. 1844">' ).css( { width: '100%', padding: '6px', 'border-radius': '4px', border: '1px solid #ccc', 'font-size': '13px', 'box-sizing': 'border-box' } );
$form.append( riga( 'Datazione:', $datazione ) );
// Descrizione
var $descrizione = $( '<textarea rows="4" placeholder="Descrizione dell\'opera...">' ).css( { width: '100%', padding: '6px', 'border-radius': '4px', border: '1px solid #ccc', 'font-size': '13px', 'box-sizing': 'border-box', resize: 'vertical' } );
$form.append( riga( 'Descrizione:', $descrizione ) );
// Macrosezione (1-2)
var $macroWrap = $( '<div>' );
var $macroSelects = [];
for ( var m = 0; m < 2; m++ ) {
var $ms = creaSelect( MACROZONE, 'museo-macro-' + m );
if ( m > 0 ) $ms.css( 'display', 'none' ).css( 'margin-top', '4px' );
$macroSelects.push( $ms );
$macroWrap.append( $ms );
}
var $aggiungiMacro = $( '<button type="button">' ).text( '+ Aggiungi macrosezione' ).css( { 'margin-top': '4px', padding: '3px 10px', cursor: 'pointer', 'font-size': '12px', display: 'block' } );
var macroVisibili = 1;
$aggiungiMacro.on( 'click', function() {
if ( macroVisibili < 2 ) {
$macroSelects[ macroVisibili ].show();
macroVisibili++;
if ( macroVisibili === 2 ) $aggiungiMacro.hide();
}
} );
$macroWrap.append( $aggiungiMacro );
$form.append( riga( 'Macrosezione:', $macroWrap ) );
// Sezione (1-4): ogni sezione dipende dalla propria macrosezione
var $sezioniWrap = $( '<div>' );
var $sezioneSelects = [];
for ( var s = 0; s < 4; s++ ) {
var $ss = creaSelect( [''], 'museo-sezione-' + s );
if ( s > 0 ) $ss.css( 'display', 'none' ).css( 'margin-top', '4px' );
$sezioneSelects.push( $ss );
$sezioniWrap.append( $ss );
}
var $aggiungiSezione = $( '<button type="button">' ).text( '+ Aggiungi sezione' ).css( { 'margin-top': '4px', padding: '3px 10px', cursor: 'pointer', 'font-size': '12px', display: 'block' } );
var sezioniVisibili = 1;
$aggiungiSezione.on( 'click', function() {
if ( sezioniVisibili < 4 ) {
$sezioneSelects[ sezioniVisibili ].show();
sezioniVisibili++;
if ( sezioniVisibili === 4 ) $aggiungiSezione.hide();
}
} );
$sezioniWrap.append( $aggiungiSezione );
$form.append( riga( 'Sezione:', $sezioniWrap ) );
// Sottosezione (1-4): ogni sottosezione dipende dalla sezione con stesso indice (o la prima disponibile)
var $sottoWrap = $( '<div>' );
var $sottoSelects = [];
for ( var u = 0; u < 4; u++ ) {
var $us = creaSelect( [''], 'museo-sotto-' + u );
if ( u > 0 ) $us.css( 'display', 'none' ).css( 'margin-top', '4px' );
$sottoSelects.push( $us );
$sottoWrap.append( $us );
}
var $aggiungiSotto = $( '<button type="button">' ).text( '+ Aggiungi sottosezione' ).css( { 'margin-top': '4px', padding: '3px 10px', cursor: 'pointer', 'font-size': '12px', display: 'block' } );
var sottoVisibili = 1;
$aggiungiSotto.on( 'click', function() {
if ( sottoVisibili < 4 ) {
$sottoSelects[ sottoVisibili ].show();
sottoVisibili++;
if ( sottoVisibili === 4 ) $aggiungiSotto.hide();
}
} );
$sottoWrap.append( $aggiungiSotto );
$form.append( riga( 'Sottosezione:', $sottoWrap ) );
// Logiche condizionali: ogni macro aggiorna la sezione dello stesso slot
function aggiornaSezione( idx ) {
var macroVal = $macroSelects[ Math.min( idx, macroVisibili - 1 ) ].val();
aggiornaSelect( $sezioneSelects[ idx ], SEZIONI_PER_MACRO[ macroVal ] || [''] );
aggiornaSelect( $sottoSelects[ idx ], [''] );
}
function aggiornaSotto( idx ) {
var sezVal = $sezioneSelects[ idx ].val();
aggiornaSelect( $sottoSelects[ idx ], SOTTOSEZIONI_PER_SEZIONE[ sezVal ] || [''] );
}
$.each( $macroSelects, function( i, $sel ) {
$sel.on( 'change', function() { aggiornaSezione( i ); } );
} );
$.each( $sezioneSelects, function( i, $sel ) {
$sel.on( 'change', function() { aggiornaSotto( i ); } );
} );
// Ubicazione combobox
var $ubicazioneWrap = creaCombobox( _ubicazioni, 'museo-ubicazione', 'Cerca o inserisci ubicazione...' );
$form.append( riga( 'Ubicazione:', $ubicazioneWrap ) );
// Tag 1-10
var $tagsWrap = $( '<div>' );
var $tagInputs = [];
for ( var t = 1; t <= 10; t++ ) {
var $tagWrap = creaCombobox( _voci, 'museo-tag-' + t, 'Tag ' + t + '...' );
if ( t > 1 ) $tagWrap.css( 'display', 'none' );
$tagInputs.push( $tagWrap.find( 'input' ) );
$tagsWrap.append( $tagWrap );
}
var $aggiungiTag = $( '<button type="button">' ).text( '+ Aggiungi tag' ).css( { 'margin-top': '4px', padding: '3px 10px', cursor: 'pointer', 'font-size': '12px' } );
var tagVisibili = 1;
$aggiungiTag.on( 'click', function() {
if ( tagVisibili < 10 ) {
$tagsWrap.find( 'div' ).eq( tagVisibili ).show();
tagVisibili++;
if ( tagVisibili === 10 ) $aggiungiTag.hide();
}
} );
$tagsWrap.append( $aggiungiTag );
$form.append( riga( 'Tag:', $tagsWrap ) );
// Pulsante upload
var $status = $( '<div>' ).css( { 'font-size': '13px', color: '#666', 'margin-top': '10px', 'text-align': 'center' } );
var $btnUpload = $( '<button type="button">' ).text( '⬆ Carica immagine' ).css( {
display: 'block', width: '100%', padding: '10px', background: '#3b5998', color: '#fff',
border: 'none', 'border-radius': '4px', cursor: 'pointer', 'font-size': '14px', 'margin-top': '16px'
} );
$btnUpload.on( 'click', function() {
var file = $fileInput[0].files[0];
if ( !file ) { $status.text( 'Seleziona un file prima di caricare.' ).css( 'color', 'red' ); return; }
var dati = {
nomeFile: file.name,
titolo: $titolo.val().trim(),
autore: $( '#museo-autore' ).val().trim(),
continente: $continente.val(),
area: $area.val(),
paese: $paese.val(),
tipologia: $tipologia.val(),
tecnica: $tecnica.val(),
periodo: $periodo.val(),
datazione: $datazione.val().trim(),
ubicazione: $( '#museo-ubicazione' ).val().trim(),
stile: $( '#museo-stile' ).val().trim(),
descrizione: $descrizione.val().trim(),
macroSezioni: $macroSelects.map( function( $s ) { return $s.val() || ''; } ),
sezioni: $sezioneSelects.map( function( $s ) { return $s.val() || ''; } ),
sottoSezioni: $sottoSelects.map( function( $s ) { return $s.val() || ''; } ),
tags: $tagInputs.map( function( $i ) { return $i.val().trim(); } )
};
var testo = costruisciTemplate( dati );
$status.text( 'Ottengo token...' ).css( 'color', '#666' );
$btnUpload.prop( 'disabled', true );
// Ottieni token CSRF
$.getJSON( API, { action: 'query', meta: 'tokens', format: 'json' }, function( data ) {
var token = data.query.tokens.csrftoken;
var formData = new FormData();
formData.append( 'action', 'upload' );
formData.append( 'filename', file.name );
formData.append( 'file', file );
formData.append( 'text', testo );
formData.append( 'comment', 'Caricato tramite Museo Uploader' );
formData.append( 'token', token );
formData.append( 'format', 'json' );
formData.append( 'ignorewarnings', '1' );
$status.text( 'Caricamento in corso...' ).css( 'color', '#666' );
$.ajax( {
url: API,
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function( resp ) {
if ( resp.upload && resp.upload.result === 'Success' ) {
$status.text( '✓ Caricato con successo: ' + file.name ).css( 'color', 'green' );
if ( typeof onSuccess === 'function' ) onSuccess( file.name );
} else {
var err = resp.upload && resp.upload.warnings ? JSON.stringify( resp.upload.warnings ) : 'Errore sconosciuto';
$status.text( 'Errore: ' + err ).css( 'color', 'red' );
$btnUpload.prop( 'disabled', false );
}
},
error: function() {
$status.text( 'Errore di rete.' ).css( 'color', 'red' );
$btnUpload.prop( 'disabled', false );
}
} );
} );
} );
$form.append( $btnUpload, $status );
$container.append( $form );
}
// ── MODALITÀ POPUP ──────────────────────────────────────────
window.apriMuseoUploader = function( onSuccess ) {
var $overlay = $( '<div id="museo-uploader-overlay">' ).css( {
position: 'fixed', top: 0, left: 0, width: '100%', height: '100%',
background: 'rgba(0,0,0,0.7)', 'z-index': 999999, 'overflow-y': 'auto'
} );
var $box = $( '<div>' ).css( {
background: '#fff', margin: '40px auto', 'max-width': '600px',
'border-radius': '8px', padding: '20px', position: 'relative'
} );
var $close = $( '<button type="button">' ).text( '✕' ).css( {
position: 'absolute', top: '10px', right: '10px', background: 'none',
border: 'none', 'font-size': '20px', cursor: 'pointer'
} ).on( 'click', function() { $overlay.remove(); } );
var $title = $( '<h3>' ).text( 'Carica immagine Museo' ).css( { margin: '0 0 15px', 'font-size': '1.1em' } );
var $loading = $( '<div>' ).text( 'Caricamento dati...' ).css( { 'text-align': 'center', padding: '20px', color: '#666' } );
$box.append( $close, $title, $loading );
$overlay.append( $box );
$overlay.on( 'click', function(e) { if ( $( e.target ).is( $overlay ) ) $overlay.remove(); } );
$( 'body' ).append( $overlay );
caricaDatiEsterni( function() {
$loading.remove();
creaForm( $box, function( nomeFile ) {
setTimeout( function() {
$overlay.remove();
if ( typeof onSuccess === 'function' ) onSuccess( nomeFile );
}, 1500 );
} );
} );
};
// ── MODALITÀ STANDALONE ─────────────────────────────────────
$( function() {
var $target = $( '#museo-uploader-standalone' );
if ( !$target.length ) return;
$target.css( { background: '#f9f9f9', border: '1px solid #ddd', 'border-radius': '8px', padding: '20px', 'max-width': '600px' } );
var $loading = $( '<div>' ).text( 'Caricamento dati...' ).css( { 'text-align': 'center', padding: '20px', color: '#666' } );
$target.append( $loading );
caricaDatiEsterni( function() {
$loading.remove();
creaForm( $target, function( nomeFile ) {
$target.prepend( $( '<div>' ).text( '✓ Immagine "' + nomeFile + '" caricata con successo!' ).css( { background: '#e6ffe6', border: '1px solid #4caf50', 'border-radius': '4px', padding: '10px', 'margin-bottom': '10px', color: '#2e7d32' } ) );
} );
} );
} );
}( mw, jQuery ) );
/* Combobox Scrittori per Maschera:Letteratura e Maschera:Fonti */
( function ( mw, $ ) {
'use strict';
var API = mw.util.wikiScript( 'api' );
var _scrittori = null;
function caricaScrittori( callback ) {
if ( _scrittori !== null ) { callback( _scrittori ); return; }
$.getJSON( API, {
action: 'query', prop: 'revisions',
titles: 'MediaWiki:Scrittori.json',
rvprop: 'content', rvslots: 'main', format: 'json'
}, function( data ) {
try {
var page = data.query.pages[ Object.keys( data.query.pages )[0] ];
var content = page.revisions[0].slots.main['*'];
_scrittori = JSON.parse( content ).scrittori || [];
} catch(e) { _scrittori = []; }
callback( _scrittori );
} ).fail( function() { _scrittori = []; callback( [] ); } );
}
function initComboboxAutore( $input, scrittori ) {
if ( $input.data( 'scrittori-init' ) ) return;
$input.data( 'scrittori-init', true );
var $lista = $( '<div>' ).css( {
position: 'absolute', background: '#fff', border: '1px solid #ccc',
'border-top': 'none', 'max-height': '200px', 'overflow-y': 'auto',
'z-index': 9999, display: 'none', 'min-width': '250px'
} );
$input.css( 'position', 'relative' ).after( $lista );
$input.on( 'input', function() {
var q = $( this ).val().toLowerCase();
$lista.empty();
if ( !q ) { $lista.hide(); return; }
var matches = scrittori.filter( function(s) {
return s.toLowerCase().indexOf( q ) !== -1;
} ).slice( 0, 15 );
if ( !matches.length ) { $lista.hide(); return; }
$.each( matches, function( i, s ) {
$( '<div>' ).text( s ).css( { padding: '5px 8px', cursor: 'pointer', 'font-size': '13px' } )
.on( 'mouseenter', function() { $( this ).css( 'background', '#f0f4ff' ); } )
.on( 'mouseleave', function() { $( this ).css( 'background', '#fff' ); } )
.on( 'mousedown', function() { $input.val( s ); $lista.hide(); } )
.appendTo( $lista );
} );
$lista.show();
} ).on( 'blur', function() {
setTimeout( function() { $lista.hide(); }, 150 );
} );
}
function initScrittori() {
var $autoreL = $( '[name="SchedaLetteratura[autore]"]' );
var $autoreF = $( '[name="Fonte[autore]"]' );
var $autoreB = $( '[name="SchedaBiblioteca[autore]"]' );
if ( !$autoreL.length && !$autoreF.length && !$autoreB.length ) return;
caricaScrittori( function( scrittori ) {
if ( $autoreL.length ) initComboboxAutore( $autoreL, scrittori );
if ( $autoreF.length ) initComboboxAutore( $autoreF, scrittori );
if ( $autoreB.length ) initComboboxAutore( $autoreB, scrittori );
} );
}
mw.hook( 'pf.formSetup' ).add( function () { initScrittori(); } );
$( function () { initScrittori(); } );
}( mw, jQuery ) );
/* Combobox Musicisti per Maschera:Musica */
( function ( mw, $ ) {
'use strict';
var API = mw.util.wikiScript( 'api' );
var _musicisti = null;
function caricaMusicisti( callback ) {
if ( _musicisti !== null ) { callback( _musicisti ); return; }
$.getJSON( API, { action: 'query', prop: 'revisions', titles: 'MediaWiki:Musicisti.json', rvprop: 'content', rvslots: 'main', format: 'json' }, function( data ) {
try {
var page = data.query.pages[ Object.keys( data.query.pages )[0] ];
_musicisti = JSON.parse( page.revisions[0].slots.main['*'] ).musicisti || [];
} catch(e) { _musicisti = []; }
callback( _musicisti );
} ).fail( function() { _musicisti = []; callback( [] ); } );
}
function initComboboxMusica( $input, lista ) {
if ( $input.data( 'cb-init' ) ) return;
$input.data( 'cb-init', true );
var $lista = $( '<div>' ).css( { position: 'absolute', background: '#fff', border: '1px solid #ccc', 'border-top': 'none', 'max-height': '200px', 'overflow-y': 'auto', 'z-index': 9999, display: 'none', 'min-width': '250px' } );
$input.css( 'position', 'relative' ).after( $lista );
$input.on( 'input', function() {
var q = $( this ).val().toLowerCase();
$lista.empty();
if ( !q ) { $lista.hide(); return; }
var matches = lista.filter( function(s) { return s.toLowerCase().indexOf( q ) !== -1; } ).slice( 0, 15 );
if ( !matches.length ) { $lista.hide(); return; }
$.each( matches, function( i, s ) {
$( '<div>' ).text( s ).css( { padding: '5px 8px', cursor: 'pointer', 'font-size': '13px' } )
.on( 'mouseenter', function() { $( this ).css( 'background', '#f0f4ff' ); } )
.on( 'mouseleave', function() { $( this ).css( 'background', '#fff' ); } )
.on( 'mousedown', function() { $input.val( s ); $lista.hide(); } )
.appendTo( $lista );
} );
$lista.show();
} ).on( 'blur', function() { setTimeout( function() { $lista.hide(); }, 150 ); } );
}
function initMusicistaCombobox() {
var $autore = $( '[name="SchedaMusica[autore]"]' );
if ( !$autore.length ) return;
caricaMusicisti( function( lista ) { initComboboxMusica( $autore, lista ); } );
}
mw.hook( 'pf.formSetup' ).add( function () { initMusicistaCombobox(); } );
$( function () { initMusicistaCombobox(); } );
}( mw, jQuery ) );
/* Combobox Registi per Maschera:Cinema */
( function ( mw, $ ) {
'use strict';
var API = mw.util.wikiScript( 'api' );
var _registi = null;
function caricaRegisti( callback ) {
if ( _registi !== null ) { callback( _registi ); return; }
$.getJSON( API, { action: 'query', prop: 'revisions', titles: 'MediaWiki:Registi.json', rvprop: 'content', rvslots: 'main', format: 'json' }, function( data ) {
try {
var page = data.query.pages[ Object.keys( data.query.pages )[0] ];
_registi = JSON.parse( page.revisions[0].slots.main['*'] ).registi || [];
} catch(e) { _registi = []; }
callback( _registi );
} ).fail( function() { _registi = []; callback( [] ); } );
}
function initComboboxRegisti( $input, lista ) {
if ( $input.data( 'cb-init' ) ) return;
$input.data( 'cb-init', true );
var $lista = $( '<div>' ).css( { position: 'absolute', background: '#fff', border: '1px solid #ccc', 'border-top': 'none', 'max-height': '200px', 'overflow-y': 'auto', 'z-index': 9999, display: 'none', 'min-width': '250px' } );
$input.css( 'position', 'relative' ).after( $lista );
$input.on( 'input', function() {
var q = $( this ).val().toLowerCase();
$lista.empty();
if ( !q ) { $lista.hide(); return; }
var matches = lista.filter( function(s) { return s.toLowerCase().indexOf( q ) !== -1; } ).slice( 0, 15 );
if ( !matches.length ) { $lista.hide(); return; }
$.each( matches, function( i, s ) {
$( '<div>' ).text( s ).css( { padding: '5px 8px', cursor: 'pointer', 'font-size': '13px' } )
.on( 'mouseenter', function() { $( this ).css( 'background', '#f0f4ff' ); } )
.on( 'mouseleave', function() { $( this ).css( 'background', '#fff' ); } )
.on( 'mousedown', function() { $input.val( s ); $lista.hide(); } )
.appendTo( $lista );
} );
$lista.show();
} ).on( 'blur', function() { setTimeout( function() { $lista.hide(); }, 150 ); } );
}
function initRegistaCombobox() {
var $regista = $( '[name="SchedaCinema[regista]"]' );
if ( !$regista.length ) return;
caricaRegisti( function( lista ) { initComboboxRegisti( $regista, lista ); } );
}
mw.hook( 'pf.formSetup' ).add( function () { initRegistaCombobox(); } );
$( function () { initRegistaCombobox(); } );
}( mw, jQuery ) );
/* =========================================================
LINK INSERTER INTELLIGENTE
- Ctrl+L su parola già scritta → popup con pagine trovate
- Digiti [[ → autocomplete inline
========================================================= */
( function ( mw, $ ) {
'use strict';
var API = mw.util.wikiScript( 'api' );
var _disambiguaCache = {};
// ── UTILITIES ────────────────────────────────────────────
function haNumero( titolo ) {
return /\s*\(\d+\)$/.test( titolo );
}
function titoloBase( titolo ) {
return titolo.replace( /\s*\(\d+\)$/, '' ).trim();
}
function cercaPagine( query, callback ) {
var risultatiSearch = [];
var risultatiDisambigua = [];
var pending = 2;
function unisci() {
pending--;
if ( pending > 0 ) return;
// Unisci: prima i risultati della ricerca, poi quelli della disambigua non già presenti
var tutti = risultatiSearch.slice();
risultatiDisambigua.forEach( function(item) {
var giàPresente = tutti.some( function(r) { return r.titolo === item.titolo; } );
if ( !giàPresente ) tutti.push( item );
} );
callback( tutti );
}
// 1. Ricerca normale (pagine esistenti)
$.getJSON( API, {
action: 'query', list: 'search',
srsearch: query, srnamespace: 0,
srlimit: 10, format: 'json'
}, function( data ) {
risultatiSearch = ( data.query && data.query.search || [] ).map( function(p) {
return { titolo: p.title, esiste: true };
} );
unisci();
} ).fail( function() { unisci(); } );
// 2. Leggi pagina di disambiguazione
$.getJSON( API, {
action: 'query', prop: 'revisions',
titles: query, rvprop: 'content',
rvslots: 'main', format: 'json'
}, function( data ) {
try {
var page = data.query.pages[ Object.keys( data.query.pages )[0] ];
if ( page.revisions ) {
var wikitext = page.revisions[0].slots.main['*'];
// Estrai tutti i link [[Titolo (N)|...]] dalla disambigua
var re = /\[\[([^\]|]+\(\d+\))[^\]]*\]\]/g;
var m;
while ( ( m = re.exec( wikitext ) ) !== null ) {
risultatiDisambigua.push( { titolo: m[1].trim(), esiste: false } );
}
// Verifica quali esistono
if ( risultatiDisambigua.length ) {
var titoli = risultatiDisambigua.map( function(r) { return r.titolo; } );
$.getJSON( API, {
action: 'query', titles: titoli.join( '|' ), format: 'json'
}, function( d ) {
var pages = d.query.pages;
var esistenti = {};
Object.keys( pages ).forEach( function(id) {
if ( id !== '-1' ) esistenti[ pages[id].title ] = true;
} );
risultatiDisambigua.forEach( function(r) {
r.esiste = !!esistenti[ r.titolo ];
} );
unisci();
} ).fail( function() { unisci(); } );
return;
}
}
} catch(e) {}
unisci();
} ).fail( function() { unisci(); } );
}
function estraiDescrizioni( base, callback ) {
if ( _disambiguaCache[ base ] ) { callback( _disambiguaCache[ base ] ); return; }
$.getJSON( API, {
action: 'query', prop: 'revisions', titles: base,
rvprop: 'content', rvslots: 'main', format: 'json'
}, function( data ) {
var desc = {};
try {
var page = data.query.pages[ Object.keys( data.query.pages )[0] ];
if ( page.revisions ) {
var wikitext = page.revisions[0].slots.main['*'];
wikitext.split( '\n' ).forEach( function( riga ) {
var m = riga.match( /\[\[([^\]|]+)\|[^\]]*\]\]'''?,\s*(.+)/ ) ||
riga.match( /\[\[([^\]]+\(\d+\))\]\]'''?,\s*(.+)/ );
if ( m ) desc[ m[1].trim() ] = m[2].replace( /'''|''/g, '' ).trim();
} );
}
} catch(e) {}
_disambiguaCache[ base ] = desc;
callback( desc );
} ).fail( function() { callback( {} ); } );
}
// ── POPUP (Ctrl+L) ───────────────────────────────────────
var $popup = null;
function chiudiPopup() {
if ( $popup ) $popup.hide();
$( document ).off( 'click.linkpopup' );
}
function inserisciLink( titolo, testoVisibile, start, end, textarea ) {
var val = textarea.value;
var link = ( titolo === testoVisibile ) ? '[[' + titolo + ']]' : '[[' + titolo + '|' + testoVisibile + ']]';
textarea.value = val.substring( 0, start ) + link + val.substring( end );
textarea.selectionStart = textarea.selectionEnd = start + link.length;
$( textarea ).trigger( 'input' );
textarea.focus();
}
function rilevaParola( textarea ) {
var pos = textarea.selectionStart;
var val = textarea.value;
if ( textarea.selectionStart !== textarea.selectionEnd ) {
return { parola: val.substring( textarea.selectionStart, textarea.selectionEnd ), start: textarea.selectionStart, end: textarea.selectionEnd };
}
var start = pos, end = pos;
while ( start > 0 && /[\wÀ-ÿ]/.test( val[start - 1] ) ) start--;
while ( end < val.length && /[\wÀ-ÿ]/.test( val[end] ) ) end++;
return { parola: val.substring( start, end ), start: start, end: end };
}
function mostraPopup( parola, risultati, start, end, textarea ) {
if ( !$popup ) {
$popup = $( '<div id="link-inserter-popup">' ).css( {
position: 'fixed', top: '50%', left: '50%',
transform: 'translate(-50%,-50%)',
background: '#fff', border: '1px solid #ccc', 'border-radius': '8px',
padding: '16px', 'min-width': '320px', 'max-width': '500px',
'z-index': 100000, 'box-shadow': '0 4px 20px rgba(0,0,0,0.2)',
'font-family': 'sans-serif', 'font-size': '14px'
} );
$( 'body' ).append( $popup );
}
$popup.empty().show();
var $header = $( '<div>' ).css( { display: 'flex', 'justify-content': 'space-between', 'align-items': 'center', 'margin-bottom': '10px' } );
$header.append(
$( '<strong>' ).text( 'Collega: "' + parola + '"' ),
$( '<button type="button">' ).text( '✕' ).css( { background: 'none', border: 'none', cursor: 'pointer', 'font-size': '18px' } ).on( 'click', chiudiPopup )
);
$popup.append( $header );
var $search = $( '<input type="text">' ).val( parola ).css( {
width: '100%', padding: '6px', 'border-radius': '4px', border: '1px solid #ccc',
'font-size': '13px', 'box-sizing': 'border-box', 'margin-bottom': '8px'
} );
$popup.append( $search );
var $lista = $( '<div>' ).css( { 'max-height': '300px', 'overflow-y': 'auto' } );
$popup.append( $lista );
function renderItems( items, parolaCorrente, desc ) {
$lista.empty();
if ( !items.length ) {
$lista.append( $( '<div>' ).text( 'Nessuna pagina trovata.' ).css( { color: '#888', padding: '6px', 'font-size': '13px' } ) );
} else {
$.each( items, function( i, item ) {
var titolo = typeof item === 'string' ? item : item.titolo;
var esiste = typeof item === 'string' ? true : item.esiste;
var bg = esiste ? '#f5f5f5' : '#fff5f5';
var $item = $( '<div>' ).css( { padding: '7px 10px', cursor: 'pointer', 'border-radius': '4px', 'margin-bottom': '2px', background: bg } );
$item.append( $( '<span>' ).text( titolo ).css( { 'font-weight': 'bold', color: esiste ? '#000' : '#c00' } ) );
if ( !esiste ) {
$item.append( $( '<span>' ).text( ' (pagina non ancora creata)' ).css( { color: '#c00', 'font-size': '11px' } ) );
}
if ( desc && desc[ titolo ] ) {
$item.append( $( '<span>' ).text( ' — ' + desc[ titolo ] ).css( { color: '#666', 'font-size': '12px' } ) );
}
$item.on( 'mouseenter', function() { $( this ).css( 'background', esiste ? '#dceeff' : '#ffe0e0' ); } )
.on( 'mouseleave', function() { $( this ).css( 'background', bg ); } )
.on( 'click', function() { inserisciLink( titolo, parolaCorrente, start, end, textarea ); chiudiPopup(); } );
$lista.append( $item );
} );
}
$lista.append(
$( '<div>' ).css( { 'border-top': '1px solid #eee', margin: '8px 0' } ),
$( '<div>' ).css( { padding: '7px 10px', cursor: 'pointer', 'border-radius': '4px', background: '#fff8e1', 'font-size': '13px', color: '#555' } )
.html( '🔗 Collega comunque a <strong>[[' + parolaCorrente + ']]</strong>' )
.on( 'mouseenter', function() { $( this ).css( 'background', '#fff3cd' ); } )
.on( 'mouseleave', function() { $( this ).css( 'background', '#fff8e1' ); } )
.on( 'click', function() { inserisciLink( parolaCorrente, parolaCorrente, start, end, textarea ); chiudiPopup(); } )
);
}
function caricaEMostra( items, parolaCorrente ) {
renderItems( items, parolaCorrente, {} );
var conNum = items.filter( function(r) {
var t = typeof r === 'string' ? r : r.titolo;
return haNumero( t );
} );
if ( conNum.length ) {
var base = titoloBase( typeof conNum[0] === 'string' ? conNum[0] : conNum[0].titolo );
estraiDescrizioni( base, function( desc ) {
renderItems( items, parolaCorrente, desc );
} );
}
}
caricaEMostra( risultati, parola );
var t;
$search.on( 'input', function() {
clearTimeout( t );
var q = $( this ).val().trim();
if ( !q ) return;
t = setTimeout( function() {
cercaPagine( q, function( nuovi ) { caricaEMostra( nuovi, q ); } );
}, 300 );
} ).focus();
setTimeout( function() {
$( document ).one( 'click.linkpopup', function(e) {
if ( !$( e.target ).closest( '#link-inserter-popup' ).length ) chiudiPopup();
} );
}, 100 );
}
// ── AUTOCOMPLETE INLINE ([[ ...) ─────────────────────────
var $ac = null;
var acItems = [];
var acIdx = -1;
var acTimer;
function nascondiAC() {
if ( $ac ) $ac.hide();
acItems = []; acIdx = -1;
}
function mostraAC( items, query, textarea ) {
if ( !$ac ) {
$ac = $( '<div id="link-ac-dd">' ).css( {
position: 'absolute', background: '#fff', border: '1px solid #ccc',
'border-radius': '4px', 'box-shadow': '0 2px 8px rgba(0,0,0,0.15)',
'z-index': 100000, 'min-width': '260px', 'max-height': '220px',
'overflow-y': 'auto', 'font-size': '13px', 'font-family': 'sans-serif'
} );
$( 'body' ).append( $ac );
}
$ac.empty();
acItems = items;
acIdx = -1;
if ( !items.length ) { nascondiAC(); return; }
function renderAC( desc ) {
$ac.empty();
$.each( items, function( i, item ) {
var titolo = typeof item === 'string' ? item : item.titolo;
var esiste = typeof item === 'string' ? true : item.esiste;
var $item = $( '<div>' ).css( { padding: '6px 10px', cursor: 'pointer' } ).attr( 'data-i', i );
$item.append( $( '<span>' ).text( titolo ).css( { 'font-weight': 'bold', color: esiste ? '#000' : '#c00' } ) );
if ( !esiste ) $item.append( $( '<span>' ).text( ' ✦' ).css( { color: '#c00', 'font-size': '10px' } ) );
if ( desc && desc[ titolo ] ) {
$item.append( $( '<span>' ).text( ' — ' + desc[ titolo ] ).css( { color: '#666', 'font-size': '11px' } ) );
}
$item.on( 'mouseenter', function() { $ac.find( 'div' ).css( 'background', '' ); $( this ).css( 'background', esiste ? '#dceeff' : '#ffe0e0' ); acIdx = i; } )
.on( 'mouseleave', function() { $( this ).css( 'background', '' ); } )
.on( 'mousedown', function(e) { e.preventDefault(); completaAC( titolo, query, textarea ); } );
$ac.append( $item );
} );
}
renderAC( {} );
var conNum = items.filter( function(r) { return haNumero( typeof r === 'string' ? r : r.titolo ); } );
if ( conNum.length ) {
var base = titoloBase( typeof conNum[0] === 'string' ? conNum[0] : conNum[0].titolo );
estraiDescrizioni( base, function( desc ) { renderAC( desc ); } );
}
// Posiziona sotto la textarea
var $ta = $( textarea );
var offset = $ta.offset();
var scrollTop = $ta.scrollTop();
var lineH = parseInt( $ta.css( 'line-height' ) ) || 18;
var val = textarea.value.substring( 0, textarea.selectionStart );
var righe = val.split( '\n' ).length;
$ac.css( { top: offset.top + righe * lineH - scrollTop + 2, left: offset.left + 8 } ).show();
}
function completaAC( titolo, query, textarea ) {
var val = textarea.value;
var pos = textarea.selectionStart;
var before = val.substring( 0, pos );
var idx = before.lastIndexOf( '[[' );
if ( idx === -1 ) return;
var link = '[[' + titolo + ']]';
textarea.value = val.substring( 0, idx ) + link + val.substring( pos );
textarea.selectionStart = textarea.selectionEnd = idx + link.length;
$( textarea ).trigger( 'input' );
textarea.focus();
nascondiAC();
}
// ── INIT SU SINGOLA TEXTAREA ─────────────────────────────
function initTextarea( textarea ) {
var $ta = $( textarea );
if ( $ta.data( 'li-init' ) ) return;
$ta.data( 'li-init', true );
// Ctrl+L
$ta.on( 'keydown.li', function(e) {
if ( e.ctrlKey && e.key === 'l' ) {
e.preventDefault();
var info = rilevaParola( textarea );
if ( !info.parola ) return;
cercaPagine( info.parola, function( items ) {
mostraPopup( info.parola, items, info.start, info.end, textarea );
} );
}
// Navigazione autocomplete
if ( $ac && $ac.is( ':visible' ) ) {
if ( e.key === 'ArrowDown' ) {
e.preventDefault();
acIdx = Math.min( acIdx + 1, acItems.length - 1 );
} else if ( e.key === 'ArrowUp' ) {
e.preventDefault();
acIdx = Math.max( acIdx - 1, 0 );
} else if ( ( e.key === 'Tab' || e.key === 'Enter' ) && acIdx >= 0 ) {
e.preventDefault();
var pos = textarea.selectionStart;
var before = textarea.value.substring( 0, pos );
var idx = before.lastIndexOf( '[[' );
var q = before.substring( idx + 2 );
var acItem = acItems[ acIdx ];
completaAC( typeof acItem === 'string' ? acItem : acItem.titolo, q, textarea );
return;
} else if ( e.key === 'Escape' ) {
nascondiAC(); return;
}
$ac.find( 'div' ).each( function( i ) {
$( this ).css( 'background', i === acIdx ? '#dceeff' : '' );
} );
}
} );
// Autocomplete [[
$ta.on( 'input.li keyup.li', function() {
var pos = textarea.selectionStart;
var before = textarea.value.substring( 0, pos );
var idx = before.lastIndexOf( '[[' );
if ( idx === -1 || before.substring( idx ).indexOf( ']]' ) !== -1 || before.substring( idx ).indexOf( '|' ) !== -1 ) {
nascondiAC(); return;
}
var query = before.substring( idx + 2 ).trim();
if ( query.length < 2 ) { nascondiAC(); return; }
clearTimeout( acTimer );
acTimer = setTimeout( function() {
cercaPagine( query, function( items ) { mostraAC( items, query, textarea ); } );
}, 250 );
} );
$ta.on( 'blur.li', function() { setTimeout( nascondiAC, 150 ); } );
}
// ── HOOK WIKIEDITOR ──────────────────────────────────────
mw.hook( 'wikiEditor.toolbarReady' ).add( function( $textarea ) {
initTextarea( $textarea[0] );
} );
function initTutte() {
document.querySelectorAll( 'textarea.createboxInput' ).forEach( function( textarea ) {
var $ta = $( textarea );
initTextarea( textarea );
var toolbar = $ta.closest( '.wikiEditor-ui' ).find( '.wikiEditor-ui-toolbar' )[0];
if ( toolbar && !toolbar.querySelector( '.link-inserter-btn' ) ) {
var btn = document.createElement( 'button' );
btn.type = 'button';
btn.className = 'link-inserter-btn';
btn.textContent = '🔗';
btn.title = 'Inserisci collegamento (Ctrl+L)';
btn.style.cssText = 'padding:2px 6px;cursor:pointer;font-size:15px;background:none;border:1px solid #ccc;border-radius:4px;margin:2px;vertical-align:middle;';
btn.addEventListener( 'click', function() {
var info = rilevaParola( textarea );
if ( !info.parola ) return;
cercaPagine( info.parola, function( items ) {
mostraPopup( info.parola, items, info.start, info.end, textarea );
} );
} );
toolbar.prepend( btn );
}
textarea.addEventListener( 'keyup', function(e) {
if ( e.ctrlKey || e.metaKey || e.altKey ) return;
var pos = textarea.selectionStart;
var before = textarea.value.substring( 0, pos );
var idx = before.lastIndexOf( '[[' );
if ( idx === -1 || before.substring( idx ).indexOf( ']]' ) !== -1 || before.substring( idx ).indexOf( '|' ) !== -1 ) {
nascondiAC(); return;
}
var query = before.substring( idx + 2 ).trim();
if ( query.length < 2 ) { nascondiAC(); return; }
clearTimeout( acTimer );
acTimer = setTimeout( function() {
cercaPagine( query, function( items ) { mostraAC( items, query, textarea ); } );
}, 250 );
}, true );
} );
}
mw.hook( 'pf.formSetup' ).add( function() { setTimeout( initTutte, 2000 ); } );
$( function() { setTimeout( initTutte, 2000 ); } );
}( mw, jQuery ) );
/* Autocomplete voci nella Maschera:Voce (senza link) */
( function ( mw, $ ) {
'use strict';
var API = mw.util.wikiScript( 'api' );
var _disambiguaCache = {};
var $acV = null;
var acVItems = [];
var acVIdx = -1;
var acVTimer;
var acVTextarea = null;
function haNumero( t ) { return /\s*\(\d+\)$/.test( t ); }
function titoloBase( t ) { return t.replace( /\s*\(\d+\)$/, '' ).trim(); }
function cercaVoci( query, callback ) {
var fromSearch = [];
var fromDisambigua = [];
var pending = 2;
function unisciERestituisci() {
pending--;
if ( pending > 0 ) return;
var tutti = fromSearch.slice();
fromDisambigua.forEach( function(r) {
if ( !tutti.some( function(s) { return s.titolo === r.titolo; } ) ) tutti.push( r );
} );
callback( tutti );
}
// 1. Prefixsearch per pagine esistenti
$.getJSON( API, { action: 'query', list: 'prefixsearch', pssearch: query, psnamespace: 0, pslimit: 8, format: 'json' }, function( data ) {
fromSearch = ( data.query && data.query.prefixsearch || [] ).map( function(p) { return { titolo: p.title, esiste: true }; } );
unisciERestituisci();
} ).fail( unisciERestituisci );
// 2. Cerca pagina di disambiguazione
$.getJSON( API, { action: 'query', list: 'prefixsearch', pssearch: query, psnamespace: 0, pslimit: 1, format: 'json' }, function( data ) {
var risultati = data.query && data.query.prefixsearch || [];
var base = risultati.length ? titoloBase( risultati[0].title ) : query;
$.getJSON( API, { action: 'query', prop: 'revisions', titles: base, rvprop: 'content', rvslots: 'main', format: 'json' }, function( data2 ) {
try {
var page = data2.query.pages[ Object.keys( data2.query.pages )[0] ];
if ( page.revisions ) {
var wikitext = page.revisions[0].slots.main['*'];
var re = /\[\[([^\]|]+\(\d+\))[^\]]*\]\]/g, m;
var titoli = [];
while ( ( m = re.exec( wikitext ) ) !== null ) titoli.push( m[1].trim() );
if ( titoli.length ) {
$.getJSON( API, { action: 'query', titles: titoli.join( '|' ), format: 'json' }, function( d ) {
var esistenti = {};
Object.keys( d.query.pages ).forEach( function(id) {
if ( id !== '-1' ) esistenti[ d.query.pages[id].title ] = true;
} );
titoli.forEach( function(t) {
fromDisambigua.push( { titolo: t, esiste: !!esistenti[t] } );
} );
unisciERestituisci();
} ).fail( unisciERestituisci );
return;
}
}
} catch(e) {}
unisciERestituisci();
} ).fail( unisciERestituisci );
} ).fail( unisciERestituisci );
}
function estraiDesc( base, callback ) {
if ( _disambiguaCache[ base ] ) { callback( _disambiguaCache[ base ] ); return; }
$.getJSON( API, { action: 'query', prop: 'revisions', titles: base, rvprop: 'content', rvslots: 'main', format: 'json' }, function( data ) {
var desc = {};
try {
var page = data.query.pages[ Object.keys( data.query.pages )[0] ];
if ( page.revisions ) {
page.revisions[0].slots.main['*'].split( '\n' ).forEach( function( riga ) {
var m = riga.match( /\[\[([^\]|]+)\|[^\]]*\]\]'''?,\s*(.+)/ ) || riga.match( /\[\[([^\]]+\(\d+\))\]\]'''?,\s*(.+)/ );
if ( m ) desc[ m[1].trim() ] = m[2].replace( /'''|''/g, '' ).trim();
} );
}
} catch(e) {}
_disambiguaCache[ base ] = desc;
callback( desc );
} ).fail( function() { callback( {} ); } );
}
function nascondiACV() {
if ( $acV ) $acV.hide();
acVItems = []; acVIdx = -1;
}
function mostraACV( items, textarea ) {
if ( !$acV ) {
$acV = $( '<div id="voce-ac-dd">' ).css( {
position: 'absolute', background: '#fff', border: '1px solid #ccc',
'border-radius': '4px', 'box-shadow': '0 2px 8px rgba(0,0,0,0.15)',
'z-index': 100000, 'min-width': '280px', 'max-height': '240px',
'overflow-y': 'auto', 'font-size': '13px', 'font-family': 'sans-serif'
} );
$( 'body' ).append( $acV );
}
$acV.empty();
acVItems = items;
acVIdx = -1;
acVTextarea = textarea;
function renderV( desc ) {
$acV.empty();
$.each( items, function( i, item ) {
var titolo = item.titolo;
var esiste = item.esiste;
var $item = $( '<div>' ).css( { padding: '6px 10px', cursor: 'pointer', 'border-left': esiste ? '3px solid #4a90d9' : '3px solid #c00' } );
$item.append( $( '<span>' ).text( titolo ).css( { 'font-weight': 'bold', color: esiste ? '#000' : '#c00' } ) );
if ( !esiste ) $item.append( $( '<span>' ).text( ' (non ancora creata)' ).css( { color: '#c00', 'font-size': '11px' } ) );
if ( desc && desc[ titolo ] ) $item.append( $( '<span>' ).text( ' — ' + desc[ titolo ] ).css( { color: '#666', 'font-size': '12px' } ) );
$item.on( 'mouseenter', function() { $acV.find( 'div' ).css( 'background', '' ); $( this ).css( 'background', '#f0f4ff' ); acVIdx = i; } )
.on( 'mouseleave', function() { $( this ).css( 'background', '' ); } )
.on( 'mousedown', function(e) {
e.preventDefault();
textarea.value = titolo;
$( textarea ).trigger( 'input' );
textarea.focus();
nascondiACV();
} );
$acV.append( $item );
} );
}
renderV( {} );
var conNum = items.filter( function(r) { return haNumero( r.titolo ); } );
if ( conNum.length ) {
estraiDesc( titoloBase( conNum[0].titolo ), function( desc ) { renderV( desc ); } );
}
var $ta = $( textarea );
var offset = $ta.offset();
$acV.css( { top: offset.top + $ta.outerHeight() + 2, left: offset.left } ).show();
}
var PATTERN_VOCE = /^Voce\[(padre|madre|fratello|fratellastro|sorellastro|consorte|personaggi)\d+\]$|^Voce\[figlio\d+\.\d+\]$/;
function initCampoVoce( input ) {
if ( input.dataset.voceAcInit ) return;
input.dataset.voceAcInit = '1';
var timer;
input.addEventListener( 'input', function() {
clearTimeout( timer );
var q = input.value.trim();
if ( q.length < 2 ) { nascondiACV(); return; }
timer = setTimeout( function() {
cercaVoci( q, function( items ) {
if ( items.length ) mostraACV( items, input );
else nascondiACV();
} );
}, 300 );
} );
input.addEventListener( 'keydown', function(e) {
if ( !$acV || !$acV.is( ':visible' ) ) return;
if ( e.key === 'ArrowDown' ) {
e.preventDefault();
acVIdx = Math.min( acVIdx + 1, acVItems.length - 1 );
} else if ( e.key === 'ArrowUp' ) {
e.preventDefault();
acVIdx = Math.max( acVIdx - 1, 0 );
} else if ( ( e.key === 'Tab' || e.key === 'Enter' ) && acVIdx >= 0 ) {
e.preventDefault();
input.value = acVItems[ acVIdx ].titolo;
$( input ).trigger( 'input' );
nascondiACV();
return;
} else if ( e.key === 'Escape' ) {
nascondiACV(); return;
}
$acV.find( 'div' ).each( function( i ) { $( this ).css( 'background', i === acVIdx ? '#f0f4ff' : '' ); } );
} );
input.addEventListener( 'blur', function() { setTimeout( nascondiACV, 150 ); } );
}
function initTuttiCampiVoce() {
document.querySelectorAll( 'input[name]' ).forEach( function( input ) {
if ( PATTERN_VOCE.test( input.name ) ) initCampoVoce( input );
} );
}
mw.hook( 'pf.formSetup' ).add( function() { setTimeout( initTuttiCampiVoce, 1000 ); } );
$( function() { setTimeout( initTuttiCampiVoce, 1000 ); } );
}( mw, jQuery ) );
/* =========================================================
WIDGET VOCI CORRELATE — Maschera:Fonte
- Input + pulsante Aggiungi in alto
- Lista alfabetica con X per rimuovere in basso
- Il valore serializzato viene salvato nel campo hidden
"SchedaBibliotecaTesto[correlate_raw]"
========================================================= */
( function ( mw, $ ) {
'use strict';
var SEP = ';';
var CSS = [
'#cv-input-row { display:flex; gap:8px; align-items:center; margin-bottom:8px; }',
'#cv-input { flex:1; border:1px solid #ccc; border-radius:4px; padding:5px 8px; font-size:13px; outline:none; }',
'#cv-input:focus { border-color:#4a90d9; box-shadow:0 0 0 2px rgba(74,144,217,.25); }',
'#cv-btn { padding:5px 14px; background:#4a90d9; color:#fff; border:none; border-radius:4px; cursor:pointer; font-size:13px; white-space:nowrap; }',
'#cv-btn:hover { background:#2d72b8; }',
'#cv-lista { border:1px solid #e0e0e0; border-radius:4px; background:#fafafa; padding:6px 8px; max-height:300px; overflow-y:auto; display:flex; flex-wrap:wrap; gap:6px; }',
'#cv-lista:empty { display:none; }',
'.cv-voce { display:inline-flex; align-items:center; padding:2px 8px; background:#e8f0fe; border:1px solid #b0c8f8; border-radius:4px; }',
'.cv-voce-nome { flex:1; font-size:13px; }',
'.cv-voce-rm { cursor:pointer; color:#aaa; font-size:14px; margin-left:4px; user-select:none; line-height:1; }',
'.cv-voce-rm:hover { color:#c00; }',
'#cv-counter { font-size:11px; color:#888; margin-top:4px; }',
'#cv-ac { position:absolute; background:#fff; border:1px solid #ccc; border-radius:4px; box-shadow:0 2px 8px rgba(0,0,0,.15); z-index:100000; min-width:260px; max-height:220px; overflow-y:auto; font-size:13px; display:none; }',
'#cv-ac .cv-ac-item { padding:6px 10px; cursor:pointer; display:flex; flex-direction:column; }',
'#cv-ac .cv-ac-titolo { font-weight:500; font-size:13px; }',
'#cv-ac .cv-ac-desc { font-size:11px; color:#666; margin-top:1px; }',
'#cv-ac .cv-ac-item:hover, #cv-ac .cv-ac-item.active { background:#dceeff; }',
'@media (max-width:560px) { #cv-lista { column-count:1; } }'
].join('\n');
var voci = [];
var $raw = null;
var $ac = null;
var acTimer;
var acItems = [];
var acIdx = -1;
var ultimoValore = '';
var API = mw.util.wikiScript( 'api' );
function aggiornaRaw() {
if ( $raw ) $raw.val( voci.filter( Boolean ).join( SEP ) );
}
function leggiRaw() {
if ( !$raw ) return;
var val = $raw.val().trim();
if ( !val ) return;
val.split( SEP ).forEach( function ( v ) {
v = v.trim();
if ( v && voci.indexOf( v ) === -1 ) voci.push( v );
} );
voci.sort( function ( a, b ) { return a.localeCompare( b, 'it' ); } );
}
function renderLista( $lista ) {
$lista.empty();
voci.forEach( function ( v, i ) {
var $riga = $( '<div class="cv-voce"></div>' );
var $nome = $( '<span class="cv-voce-nome"></span>' ).text( v );
var $rm = $( '<span class="cv-voce-rm" title="Rimuovi">×</span>' );
$rm.on( 'mousedown', function ( e ) {
e.preventDefault();
voci.splice( i, 1 );
aggiornaRaw();
renderLista( $lista );
aggiornaContatore();
} );
$riga.append( $nome ).append( $rm );
$lista.append( $riga );
} );
aggiornaContatore();
}
function aggiornaContatore() {
$( '#cv-counter' ).text( voci.length + ' voce/i nell\'elenco' );
}
function aggiungiVoce( val, $lista, $input ) {
// Gestisce input multiplo separato da virgola
if ( val.indexOf( ',' ) !== -1 ) {
val.split( ',' ).forEach( function( v ) {
aggiungiVoce( v.trim(), $lista, null );
} );
if ( $input ) { $input.val( '' ); ultimoValore = ''; $input.focus(); }
return;
}
val = val.replace( /^\[\[|\]\]$/g, '' ).trim();
if ( !val ) return;
if ( voci.indexOf( val ) !== -1 ) {
$lista.find( '.cv-voce-nome' ).filter( function () {
return $( this ).text() === val;
} ).parent().css( 'background', '#fffbe6' ).delay( 600 ).queue( function ( next ) {
$( this ).css( 'background', '' ); next();
} );
$input.val( '' );
ultimoValore = '';
return;
}
voci.push( val );
voci.sort( function ( a, b ) { return a.localeCompare( b, 'it' ); } );
aggiornaRaw();
renderLista( $lista );
if ( $input ) { $input.val( '' ); ultimoValore = ''; $input.focus(); }
nascondiAC();
}
// Controlla se una pagina è una disambigua e ne estrae le voci
function espandiDisambigua( titolo, callback ) {
$.getJSON( API, {
action: 'query',
titles: titolo,
prop: 'revisions',
rvprop: 'content',
rvslots: 'main',
format: 'json'
}, function ( data ) {
var pages = data.query && data.query.pages || {};
var wikitext = '';
$.each( pages, function( id, page ) {
var slots = page.revisions && page.revisions[0] && page.revisions[0].slots;
wikitext = ( slots && slots.main && slots.main['*'] ) || '';
} );
// Verifica se è una disambigua
if ( wikitext.indexOf( '{{disambigua}}' ) === -1 &&
wikitext.indexOf( '{{Disambigua}}' ) === -1 ) {
callback( null ); // non è una disambigua
return;
}
// Estrae le voci: *'''[[Titolo (N)|Label]]''', descrizione
var voci = [];
var re = /\*'{0,3}\[\[([^\]|]+)(?:\|[^\]]+)?\]\]'{0,3}(?:,?\s*(.+))?/g;
var match;
while ( ( match = re.exec( wikitext ) ) !== null ) {
var link = match[1].trim();
var desc = ( match[2] || '' ).trim()
.replace( /'{2,3}/g, '' ) // rimuove grassetto/corsivo
.replace( /__NOTOC__/g, '' ) // rimuove magic words
.trim();
if ( link ) voci.push( { titolo: link, desc: desc } );
}
callback( voci.length ? voci : null );
} ).fail( function () { callback( null ); } );
}
function cercaPagine( query, callback ) {
$.getJSON( API, {
action: 'query', list: 'search', srsearch: query,
srnamespace: 0, srlimit: 10, format: 'json'
}, function ( data ) {
callback( ( data.query && data.query.search || [] ).map( function ( p ) { return p.title; } ) );
} ).fail( function () { callback( [] ); } );
}
// Seleziona la voce e aggiorna l'input
function selezionaVoce( titolo, $input ) {
var full = $input.val();
var parts = full.split( ',' );
parts[ parts.length - 1 ] = ' ' + titolo;
var newVal = parts.join( ',' ).replace( /^,\s*/, '' );
$input.val( newVal );
ultimoValore = newVal;
nascondiAC();
$input.focus();
}
function mostraAC( items, $input, $lista ) {
if ( !$ac ) { $ac = $( '<div id="cv-ac"></div>' ); $( 'body' ).append( $ac ); }
$ac.empty();
acItems = [];
acIdx = -1;
if ( !items.length ) { $ac.hide(); return; }
var pending = items.length;
var risultati = new Array( items.length );
items.forEach( function ( titolo, idx ) {
espandiDisambigua( titolo, function ( vociDisambigua ) {
if ( vociDisambigua ) {
// È una disambigua: espande le voci
risultati[ idx ] = vociDisambigua.map( function ( v ) {
return { titolo: v.titolo, desc: v.desc, isDisambigua: true };
} );
} else {
risultati[ idx ] = [ { titolo: titolo, desc: '', isDisambigua: false } ];
}
pending--;
if ( pending === 0 ) renderAC( risultati, $input, $lista );
} );
} );
}
function renderAC( risultati, $input, $lista ) {
$ac.empty();
acItems = [];
// Raccoglie tutti i titoli già coperti dalle disambigue
var coperteDaDisambigua = {};
risultati.forEach( function ( gruppo ) {
if ( gruppo && gruppo.length > 1 && gruppo[0].isDisambigua ) {
gruppo.forEach( function ( item ) {
coperteDaDisambigua[ item.titolo ] = true;
} );
}
} );
// Appiattisce i risultati escludendo i duplicati già coperti
risultati.forEach( function ( gruppo ) {
( gruppo || [] ).forEach( function ( item ) {
// Salta le voci singole già presenti in una disambigua
if ( !item.isDisambigua && coperteDaDisambigua[ item.titolo ] ) return;
acItems.push( item.titolo );
var $item = $( '<div class="cv-ac-item"></div>' );
var $titolo = $( '<span class="cv-ac-titolo"></span>' ).text( item.titolo );
$item.append( $titolo );
if ( item.desc ) {
var $desc = $( '<span class="cv-ac-desc"></span>' ).text( ' — ' + item.desc );
$item.append( $desc );
}
$item.on( 'mousedown', function ( e ) {
e.preventDefault();
selezionaVoce( item.titolo, $input );
} );
$ac.append( $item );
} );
} );
if ( !acItems.length ) { $ac.hide(); return; }
var offset = $input.offset();
$ac.css( { top: offset.top + $input.outerHeight() + 2, left: offset.left } ).show();
}
function nascondiAC() {
if ( $ac ) $ac.hide();
acItems = []; acIdx = -1;
}
function evidenziaAC() {
if ( !$ac ) return;
$ac.find( '.cv-ac-item' ).removeClass( 'active' ).each( function ( i ) {
if ( i === acIdx ) $( this ).addClass( 'active' );
} );
}
function costruisciWidget( $wrapper ) {
$raw = $( '[name="SchedaBibliotecaTesto[correlate_raw]"],[name="SchedaLetteraturaTesto[correlate_raw]"],[name="SchedaCinemaTesto[correlate_raw]"],[name="SchedaMusicaTesto[correlate_raw]"]' ).first();
if ( !$raw.length ) return;
leggiRaw();
var $row = $( '<div id="cv-input-row"></div>' );
var $input = $( '<input id="cv-input" type="text" placeholder="Digita una voce\u2026" autocomplete="off">' );
var $btn = $( '<button id="cv-btn" type="button">+ Aggiungi</button>' );
var $lista = $( '<div id="cv-lista"></div>' );
var $counter = $( '<div id="cv-counter"></div>' );
$row.append( $input ).append( $btn );
$wrapper.append( $row ).append( $lista ).append( $counter );
renderLista( $lista );
$input.on( 'keyup', function () {
ultimoValore = $input.val();
} );
$btn.on( 'mousedown', function ( e ) {
e.preventDefault();
var val = ultimoValore.trim();
if ( acIdx >= 0 && $ac && $ac.is( ':visible' ) ) {
aggiungiVoce( acItems[ acIdx ], $lista, $input );
} else if ( val ) {
aggiungiVoce( val, $lista, $input );
}
} );
$input.on( 'keydown', function ( e ) {
if ( $ac && $ac.is( ':visible' ) ) {
if ( e.key === 'ArrowDown' ) { e.preventDefault(); acIdx = Math.min( acIdx + 1, acItems.length - 1 ); evidenziaAC(); }
else if ( e.key === 'ArrowUp' ) { e.preventDefault(); acIdx = Math.max( acIdx - 1, 0 ); evidenziaAC(); }
else if ( e.key === 'Escape' ) { nascondiAC(); }
}
} );
$input.on( 'input', function () {
var full = $input.val();
var parts = full.split( ',' );
var q = parts[ parts.length - 1 ].trim();
if ( q.length < 2 ) { nascondiAC(); return; }
clearTimeout( acTimer );
acTimer = setTimeout( function () {
cercaPagine( q, function ( items ) { mostraAC( items, $input, $lista ); } );
}, 250 );
} );
$input.on( 'blur', function () { setTimeout( nascondiAC, 150 ); } );
}
function init() {
// Cerca correlate_raw in tutti i template supportati
var $rawCheck = $( '[name="SchedaBibliotecaTesto[correlate_raw]"],[name="SchedaLetteraturaTesto[correlate_raw]"],[name="SchedaCinemaTesto[correlate_raw]"],[name="SchedaMusicaTesto[correlate_raw]"]' );
if ( !$rawCheck.length ) return;
$rawCheck.closest( 'tr' ).hide();
var $titolo = $( 'div' ).filter( function () {
return $( this ).text().trim() === 'VOCI CORRELATE';
} ).first();
var $wrapper = $( '<div id="cv-wrapper" style="padding:8px 0 12px;"></div>' );
if ( $titolo.length ) {
$titolo.after( $wrapper );
} else {
$rawCheck.closest( 'tr' ).before( $wrapper );
}
costruisciWidget( $wrapper );
}
$( 'head' ).append( '<style>' + CSS + '</style>' );
mw.hook( 'pf.formSetup' ).add( function () { setTimeout( init, 500 ); } );
$( function () { setTimeout( init, 500 ); } );
}( mw, jQuery ) );
/* =====================
FIX LAYOUT COLLASSABILI accanto alla scheda laterale
===================== */
$(function() {
document.querySelectorAll('.mw-collapsible').forEach(function(el) {
el.style.overflow = 'hidden';
});
});
/* =========================================================
BIBLIOTECA WIKIEDITOR TOOLS
- Pulsante 🔗 (riusa link-inserter globale)
- Highlight parole potenziali link
- Versificatore {{v|1|N}}
Attivo in: ns 504 (Biblioteca), 0 (Principale/Voce),
516 (Racconti), 520 (Letteratura),
522 (Musica), 524 (Cinema)
In ns 504 il versificatore è attivo; negli altri no.
========================================================= */
( function ( mw, $ ) {
'use strict';
var NS_BIBLIOTECA = 504;
var NS_CON_HL = [ 0, 504, 516, 520, 522, 524 ];
var API = mw.util.wikiScript( 'api' );
var nsCorrente = mw.config.get( 'wgNamespaceNumber' );
var action = mw.config.get( 'wgAction' );
// Nella maschera Page Forms (Special:FormEdit) wgNamespaceNumber è -1
// e wgAction è 'view'. Rileviamo la maschera dal nome della Special page.
var inMaschera = ( mw.config.get( 'wgCanonicalSpecialPageName' ) === 'FormEdit' );
// In modalità maschera, ricaviamo il namespace reale dal titolo della pagina
// che viene modificata (es. "Special:FormEdit/Voce/Apollo" → ns 0)
if ( inMaschera ) {
// Mappa form → namespace (deve essere allineata a NS_CON_HL)
var formNsMap = { 'Voce': 0, 'SchedaBiblioteca': 504, 'Racconto': 516,
'SchedaLetteratura': 520, 'SchedaMusica': 522, 'SchedaCinema': 524 };
var pathParts = mw.config.get( 'wgPageName' ).split( '/' ); // ["Special:FormEdit","Voce","Apollo"]
if ( pathParts.length >= 2 ) {
var nomeForm = pathParts[1];
if ( formNsMap.hasOwnProperty( nomeForm ) ) {
nsCorrente = formNsMap[ nomeForm ];
}
}
if ( NS_CON_HL.indexOf( nsCorrente ) === -1 ) return; // form non supportato
} else {
// Modifica wikieditor normale
if ( NS_CON_HL.indexOf( nsCorrente ) === -1 ) return;
if ( action !== 'edit' && action !== 'submit' ) return;
}
// CSS
$( 'head' ).append( '<style>' +
( ( nsCorrente === NS_BIBLIOTECA && !inMaschera ) ? '#wpTextbox1 { min-height: 600px !important; height: 600px !important; }' : '' ) +
'.wikiEditor-ui-text { height: auto !important; }' +
'.bib-hl-panel { position: relative; z-index: 5; }' +
'</style>' );
/* ── 1. RIUSO DEL LINK-INSERTER GLOBALE ──────────────────
Il modulo globale (link-inserter) già si aggancia a
mw.hook('wikiEditor.toolbarReady') — quindi il pulsante 🔗
e il Ctrl+L funzionano automaticamente anche qui.
Non serve fare nulla in più per questo punto.
─────────────────────────────────────────────────────── */
/* ── 2. HIGHLIGHT PAROLE POTENZIALI LINK ─────────────────
Strategia: div overlay trasparente sovrapposto alla textarea,
stesso font/padding, con <mark> sulle parole trovate.
Click su una parola evidenziata → popup link/ignora.
──────────────────────────────────────────────────────── */
// Cache esistenza pagine (solo memoria, si azzera ad ogni load)
var paginaCache = {};
// Cache descrizioni disambigua
var _descCache = {};
// Timer debounce
var hlTimer = null;
// ── AUTOLINK DATA ─────────────────────────────────────────
// Struttura dati caricata da MediaWiki:AutolinkData (JSON).
// Formato:
// {
// "nonLink": { "Parola": true, ... }, // ignora sempre
// "nonLinkPag": { "NomePagina": { "Parola": true } }, // ignora in pagina
// "scelte": { // apprendimento link esatti
// "Parola": {
// "conferme": 5, // quante volte collegato
// "rifiuti": 1, // quante volte rifiutato (ignora questa/sempre)
// "autolink": false, // se true → link diretto senza popup
// "autoDisabilitato": false // override manuale utente
// }
// },
// "contesti": { // apprendimento disambigua contestuale
// "Parola": [
// { "paroleChiave": ["Arjuna","guerra","Narada"], "scelta": "Krishna (1)", "peso": 3 },
// ...
// ]
// }
// }
var AUTOLINK_PAGE = 'MediaWiki:AutolinkData';
var AUTOLINK_SOGLIA_CONFERME = 5; // conferme per attivare autolink
var AUTOLINK_PESO_RIFIUTO = 3; // 1 rifiuto = N conferme in negativo
var AUTOLINK_SOGLIA_CONTESTO = 0.4; // confidenza minima per suggerire senza Claude
var AUTOLINK_PROXY_URL = '/claude-proxy.php';
// Dati in memoria (caricati all'avvio)
var alData = { nonLink: {}, nonLinkPag: {}, scelte: {}, contesti: {} };
var alDirty = false; // true se ci sono modifiche da salvare
var alSaveTimer = null; // timer per salvataggio lazy
var alCaricato = false; // true quando il caricamento iniziale è completato
var alCaricando = false;
var alCodaCallbacks = []; // callbacks in attesa del caricamento
// nonLink locale — persiste solo per questa pagina specifica (alias su alData)
var paginaCorrente = mw.config.get( 'wgPageName' );
var nonLinkPag = {}; // verrà popolato dopo il caricamento
// nonLinkOccorrenze — frammenti di contesto di occorrenze saltate per pagina
// { "NomePagina": { "Parola": ["...contesto1...", "...contesto2..."] } }
var nonLinkOccorrenze = {};
// ── Caricamento da MediaWiki:AutolinkData ─────────────────
function alCarica( callback ) {
if ( alCaricato ) { if ( callback ) callback(); return; }
if ( alCaricando ) { if ( callback ) alCodaCallbacks.push( callback ); return; }
alCaricando = true;
if ( callback ) alCodaCallbacks.push( callback );
$.getJSON( mw.util.wikiScript( 'api' ), {
action: 'query', prop: 'revisions',
titles: AUTOLINK_PAGE,
rvprop: 'content', rvslots: 'main', format: 'json'
} ).done( function( data ) {
var pages = data.query && data.query.pages || {};
var page = pages[ Object.keys( pages )[0] ];
if ( page && page.revisions ) {
try {
var testo = page.revisions[0].slots.main['*'];
// Il contenuto della pagina è JSON racchiuso in <nowiki>...</nowiki>
var m = testo.match( /<nowiki>([\s\S]*?)<\/nowiki>/ );
var json = m ? m[1] : testo;
var parsed = JSON.parse( json );
alData.nonLink = parsed.nonLink || {};
alData.nonLinkPag = parsed.nonLinkPag || {};
alData.nonLinkOccorrenze= parsed.nonLinkOccorrenze|| {};
alData.scelte = parsed.scelte || {};
alData.contesti = parsed.contesti || {};
} catch(e) { /* pagina vuota o non ancora creata, ok */ }
}
nonLinkPag = alData.nonLinkPag[ paginaCorrente ] || {};
nonLinkOccorrenze = ( alData.nonLinkOccorrenze || {} )[ paginaCorrente ] || {};
alCaricato = true;
alCaricando = false;
alCodaCallbacks.forEach( function(cb) { cb(); } );
alCodaCallbacks = [];
} ).fail( function() {
// Se fallisce il caricamento usa struttura vuota
alCaricato = true;
alCaricando = false;
alCodaCallbacks.forEach( function(cb) { cb(); } );
alCodaCallbacks = [];
} );
}
// ── Salvataggio lazy su MediaWiki:AutolinkData ────────────
function alSalvaOra() {
if ( !alDirty ) return;
alDirty = false;
clearTimeout( alSaveTimer );
alData.nonLinkPag[ paginaCorrente ] = nonLinkPag;
alData.nonLinkOccorrenze = alData.nonLinkOccorrenze || {};
alData.nonLinkOccorrenze[ paginaCorrente ] = nonLinkOccorrenze;
// Pulisce pagine vuote
Object.keys( alData.nonLinkPag ).forEach( function(p) {
if ( !Object.keys( alData.nonLinkPag[p] ).length ) delete alData.nonLinkPag[p];
} );
Object.keys( alData.nonLinkOccorrenze ).forEach( function(p) {
if ( !Object.keys( alData.nonLinkOccorrenze[p] ).length ) delete alData.nonLinkOccorrenze[p];
} );
var contenuto = '<nowiki>' + JSON.stringify( alData ) + '</nowiki>';
new mw.Api().postWithToken( 'csrf', {
action: 'edit',
title: AUTOLINK_PAGE,
text: contenuto,
summary: 'Aggiornamento automatico dati autolink',
bot: true
} ).fail( function() {
alDirty = true;
clearTimeout( alSaveTimer );
alSaveTimer = setTimeout( alSalvaOra, 30000 );
} );
}
function alSalva() {
alDirty = true;
clearTimeout( alSaveTimer );
alSaveTimer = setTimeout( alSalvaOra, 2000 ); // salva dopo 2 secondi di inattività
}
// Salva immediatamente prima che l'utente lasci la pagina
$( window ).on( 'beforeunload', function() {
if ( alDirty ) alSalvaOra();
} );
// ── API pubblica nonLink ──────────────────────────────────
// NON usare alias — puntare sempre a alData.nonLink direttamente
// perché alCarica() sostituisce l'oggetto e un alias resterebbe stale.
function aggiungiNonLink( parola ) {
alData.nonLink[ parola ] = true;
alRegistraRifiuto( parola );
alSalvaOra(); // salvataggio immediato, non lazy
}
function rimuoviNonLink( parola ) {
delete alData.nonLink[ parola ];
alSalvaOra();
}
function aggiungiNonLinkPag( parola ) {
nonLinkPag[ parola ] = true;
alRegistraRifiuto( parola );
alSalvaOra();
}
function rimuoviNonLinkPag( parola ) {
delete nonLinkPag[ parola ];
alSalvaOra();
}
// ── Apprendimento scelte (link esatti) ────────────────────
function alRegistraConferma( parola ) {
if ( !alData.scelte[ parola ] ) alData.scelte[ parola ] = { conferme: 0, rifiuti: 0, autolink: false, autoDisabilitato: false };
alData.scelte[ parola ].conferme++;
// Verifica se raggiunge soglia autolink
var s = alData.scelte[ parola ];
if ( !s.autoDisabilitato && !s.autolink ) {
var score = s.conferme - ( s.rifiuti * AUTOLINK_PESO_RIFIUTO );
if ( score >= AUTOLINK_SOGLIA_CONFERME ) {
s.autolink = true;
}
}
alSalva();
}
function alRegistraRifiuto( parola ) {
if ( !alData.scelte[ parola ] ) alData.scelte[ parola ] = { conferme: 0, rifiuti: 0, autolink: false, autoDisabilitato: false };
alData.scelte[ parola ].rifiuti++;
// Un rifiuto può disattivare l'autolink
var s = alData.scelte[ parola ];
var score = s.conferme - ( s.rifiuti * AUTOLINK_PESO_RIFIUTO );
if ( score < AUTOLINK_SOGLIA_CONFERME ) {
s.autolink = false;
}
alSalva();
}
function alToggleAutolink( parola ) {
if ( !alData.scelte[ parola ] ) alData.scelte[ parola ] = { conferme: 0, rifiuti: 0, autolink: false, autoDisabilitato: false };
var s = alData.scelte[ parola ];
if ( s.autolink ) {
s.autolink = false;
s.autoDisabilitato = true;
} else {
s.autolink = true;
s.autoDisabilitato = false;
}
alSalva();
return s.autolink;
}
function alIsAutolink( parola ) {
var s = alData.scelte[ parola ];
return s && s.autolink && !s.autoDisabilitato;
}
// ── Apprendimento contestuale (disambigua) ─────────────────
// Estrae le parole chiave significative dal contesto (50 parole prima + dopo)
function alEstraiContesto( testo, posizione, parola ) {
var FINESTRA = 300; // caratteri prima e dopo
var prima = testo.substring( Math.max( 0, posizione - FINESTRA ), posizione );
var dopo = testo.substring( posizione + parola.length, posizione + parola.length + FINESTRA );
var tutto = ( prima + ' ' + dopo ).toLowerCase();
// Rimuove markup wiki e punteggiatura
tutto = tutto.replace( /\{\{[^}]*\}\}|\[\[|\]\]|''|<[^>]+>/g, ' ' );
tutto = tutto.replace( /[^a-zàèéìòùáéíóú\s]/gi, ' ' );
var parole = tutto.split( /\s+/ ).filter( function(p) {
return p.length > 3 && !( p.toLowerCase() === parola.toLowerCase() );
} );
// Prendi le 20 più frequenti (semplice conteggio)
var freq = {};
parole.forEach( function(p) { freq[p] = ( freq[p] || 0 ) + 1; } );
return Object.keys( freq ).sort( function(a,b) { return freq[b]-freq[a]; } ).slice( 0, 20 );
}
function alRegistraContesto( parola, paroleChiave, scelta ) {
if ( !alData.contesti[ parola ] ) alData.contesti[ parola ] = [];
var lista = alData.contesti[ parola ];
// Cerca se esiste già una entry con questa scelta
var entry = null;
for ( var i = 0; i < lista.length; i++ ) {
if ( lista[i].scelta === scelta ) { entry = lista[i]; break; }
}
if ( entry ) {
// Aggiorna parole chiave con peso incrementale
paroleChiave.forEach( function(pk) {
if ( entry.paroleChiave.indexOf( pk ) === -1 ) entry.paroleChiave.push( pk );
} );
entry.peso++;
} else {
lista.push( { paroleChiave: paroleChiave, scelta: scelta, peso: 1 } );
}
alSalva();
}
// Calcola la scelta più probabile in base al contesto attuale
// Restituisce { scelta, confidenza } o null se non abbastanza dati
function alSuggerisciDaContesto( parola, paroleChiaveAttuali ) {
var lista = alData.contesti[ parola ];
if ( !lista || !lista.length ) return null;
var punteggioTotale = 0;
var punteggi = {};
lista.forEach( function( entry ) {
var match = 0;
entry.paroleChiave.forEach( function(pk) {
if ( paroleChiaveAttuali.indexOf( pk ) !== -1 ) match++;
} );
if ( match > 0 ) {
var score = ( match / entry.paroleChiave.length ) * entry.peso;
punteggi[ entry.scelta ] = ( punteggi[ entry.scelta ] || 0 ) + score;
punteggioTotale += score;
}
} );
if ( !punteggioTotale ) return null;
var migliore = null, maxScore = 0;
Object.keys( punteggi ).forEach( function(s) {
if ( punteggi[s] > maxScore ) { maxScore = punteggi[s]; migliore = s; }
} );
var confidenza = maxScore / punteggioTotale;
return { scelta: migliore, confidenza: confidenza };
}
// Chiama il proxy Claude per la disambiguazione contestuale
function alChiediClaude( parola, contesto, opzioni, callback ) {
$.ajax( {
url: AUTOLINK_PROXY_URL,
method: 'POST',
contentType: 'application/json',
data: JSON.stringify( { parola: parola, contesto: contesto, opzioni: opzioni } ),
timeout: 15000
} ).done( function( data ) {
callback( data.suggerimento || 'INCERTO' );
} ).fail( function() {
callback( 'INCERTO' );
} );
}
// Pulizia automatica: se una parola ignorata viene usata come link, riabilitala
function autoCleanNonLink( testo ) {
var reLink = /\[\[([^\]|]+)(?:\|[^\]]*)?\]\]/g;
var m;
var changed = false;
while ( ( m = reLink.exec( testo ) ) !== null ) {
var titolo = m[1].trim();
if ( alData.nonLink[ titolo ] ) {
delete alData.nonLink[ titolo ];
changed = true;
}
var parteFinale = titolo.split(':').pop().trim();
if ( parteFinale !== titolo && alData.nonLink[ parteFinale ] ) {
delete alData.nonLink[ parteFinale ];
changed = true;
}
}
if ( changed ) alSalva();
}
// Parole grammaticali da ignorare — sia maiuscole (inizio verso) che minuscole
var STOP_WORDS = (function() {
var w = [
'il','lo','la','i','gli','le','un','uno','una',
'di','del','dello','della','dei','degli','delle',
'a','ad','al','allo','alla','ai','agli','alle',
'da','dal','dallo','dalla','dai','dagli','dalle',
'in','nel','nello','nella','nei','negli','nelle',
'su','sul','sullo','sulla','sui','sugli','sulle',
'con','col','coi','per','tra','fra',
'e','ed','o','ma','se','che','chi','cui',
'non','si','ci','vi','ne',
'sono','fu','ha','ho','hai','hanno','aveva','avevo','avevano',
'sia','fosse','essere',
'poi','come','dove','quando','mentre',
'anche','ancora','sempre','mai','allora','quindi','dunque',
'suo','sua','suoi','sue','mio','mia','tuo','tua','loro',
'questo','questa','questi','queste',
'quello','quella','quelli','quelle',
'ogni','tutto','tutta','tutti','tutte',
'altro','altra','altri','altre',
'quale','quali','quanto','quanta','quanti','quante',
'tale','tali','nessuno','nessuna','chiunque','qualunque',
'ecco','qui','ora','pur','ben',
'io','tu','egli','ella','esso','essa','noi','voi'
];
var s = {};
w.forEach(function(x){
s[x] = 1;
s[ x.charAt(0).toUpperCase() + x.slice(1) ] = 1;
});
return s;
}());
// ── ECCEZIONI MINUSCOLE ───────────────────────────────────
// Parole minuscole da trattare SEMPRE come candidati link,
// anche senza verificare l'esistenza della pagina.
// Gestite tramite la pagina MediaWiki:AutolinkEccezioni
// (una parola per riga; righe che iniziano con # vengono ignorate).
var ECCEZIONI_PAGE = 'MediaWiki:AutolinkEccezioni';
var eccezioniMinuscole = {};
var eccezioniCaricate = false;
var eccezioniCaricando = false;
var eccezioniCoda = [];
function caricaEccezioni( callback ) {
if ( eccezioniCaricate ) { if ( callback ) callback(); return; }
if ( eccezioniCaricando ) { if ( callback ) eccezioniCoda.push( callback ); return; }
eccezioniCaricando = true;
if ( callback ) eccezioniCoda.push( callback );
$.getJSON( mw.util.wikiScript( 'api' ), {
action: 'query', prop: 'revisions',
titles: ECCEZIONI_PAGE,
rvprop: 'content', rvslots: 'main', format: 'json'
} ).done( function( data ) {
var pages = data.query && data.query.pages || {};
var page = pages[ Object.keys( pages )[0] ];
if ( page && page.revisions ) {
try {
var testo = page.revisions[0].slots.main['*'];
testo.split( '\n' ).forEach( function( riga ) {
riga = riga.trim();
// Salta righe vuote, commenti (#) e markup wiki
if ( !riga || riga.charAt(0) === '#' || riga.charAt(0) === '<' ) return;
// Rimuove eventuale markup residuo
riga = riga.replace( /\[\[|\]\]|'''|''/g, '' ).trim();
if ( riga ) eccezioniMinuscole[ riga ] = true;
} );
} catch(e) {}
}
eccezioniCaricate = true;
eccezioniCaricando = false;
eccezioniCoda.forEach( function(cb) { cb(); } );
eccezioniCoda = [];
} ).fail( function() {
eccezioniCaricate = true;
eccezioniCaricando = false;
eccezioniCoda.forEach( function(cb) { cb(); } );
eccezioniCoda = [];
} );
}
// Estrae candidati dal testo.
// Logica:
// 1. Raccoglie i titoli già linkati: [[Titolo]] e [[Titolo|Testo]]
// 2. Raccoglie tutte le parole con maiuscola e le minuscole ≥5 car
// 3. Esclude quelle già linkate (a meno che appaiano ANCHE fuori dai link)
function estraiCandidati( testo ) {
// Paso 1 — titoli già linkati (non vanno proposti di nuovo)
var giàLinkati = {};
var reLink = /\[\[([^\]|]+)(?:\|[^\]]*)?\]\]/g;
var mm;
while ( ( mm = reLink.exec( testo ) ) !== null ) {
var titolo = mm[1].trim();
// Conta quante volte appare come link
giàLinkati[ titolo ] = ( giàLinkati[ titolo ] || 0 ) + 1;
}
// Passo 2 — testo pulito: rimuove link (mantiene testo visibile),
// template e tag, ma segna dove erano i link
// così possiamo capire se una parola appare SOLO come link
var testoSenzaLink = testo
.replace( /\[\[[^\]|]+\|([^\]]+)\]\]/g, ' $1 ' ) // [[Dest|Vis]] → Vis
.replace( /\[\[([^\]]+)\]\]/g, ' $1 ' ) // [[Titolo]] → Titolo
.replace( /\{\{[^}]*\}\}/g, ' ' )
.replace( /<[^>]+>/g, ' ' );
// Passo 3 — testo libero (fuori dai link): sostituisce .... con spazi
var testoLibero = testoSenzaLink.replace( / [^ ]* /g, ' ' );
var set = {};
var m;
// Parole con maiuscola nel testo LIBERO (fuori dai link)
// Sequenze con maiuscola nel testo libero (fuori dai link)
var reMaius = /([A-ZÀÈÉÌÒÙÁÉÍÓÚ][a-zA-ZÀ-ÿ]{2,}(?:\s+[A-ZÀÈÉÌÒÙÁÉÍÓÚ][a-zA-ZÀ-ÿ]{2,}){0,3})/g;
while ( ( m = reMaius.exec( testoLibero ) ) !== null ) {
var p = m[1].trim();
if ( p.length >= 3 && !STOP_WORDS[ p ] && !alData.nonLink[ p ] && !nonLinkPag[ p ] ) set[ p ] = true;
}
// Parole minuscole ≥5 car nel testo libero
var reMin = / ([a-zàèéìòùáéíóú]{5,}) /g;
while ( ( m = reMin.exec( testoLibero ) ) !== null ) {
var pw = m[1];
if ( !STOP_WORDS[ pw ] && !alData.nonLink[ pw ] && !nonLinkPag[ pw ] ) set[ pw ] = true;
}
// Parole con maiuscola nel testo dei link (dentro ... )
// che appaiono ANCHE nel testo libero → già incluse sopra
// che appaiono SOLO come link → NON le includiamo (già linkate)
// Ma se il titolo del link appare nel testo libero con iniziale maiuscola
// come parola separata → già catturato sopra
// Filtra parole le cui tutte le occorrenze libere sono già saltate permanentemente
return Object.keys( set ).filter( function( parola ) {
return trovaPosizioneLiberaSuccessiva( testo, parola, -1 ) !== -1;
} );
}
// Verifica esistenza esatta su API in batch da 50
function verificaEsistenza( parole, callback ) {
var daVerificare = parole.filter( function(p) { return paginaCache[p] === undefined; } );
if ( !daVerificare.length ) { callback(); return; }
var batches = [];
for ( var i = 0; i < daVerificare.length; i += 50 ) {
batches.push( daVerificare.slice( i, i + 50 ) );
}
var pending = batches.length;
batches.forEach( function( batch ) {
batch.forEach( function(p) { paginaCache[p] = 'pending'; } );
$.getJSON( API, {
action: 'query', titles: batch.join( '|' ), format: 'json'
}, function( data ) {
var pages = ( data.query && data.query.pages ) || {};
var esistenti = {};
Object.keys( pages ).forEach( function(id) {
if ( id !== '-1' ) esistenti[ pages[id].title ] = true;
} );
batch.forEach( function(p) {
paginaCache[p] = !!( esistenti[p] || esistenti[ p.charAt(0).toUpperCase() + p.slice(1) ] );
} );
pending--;
if ( pending === 0 ) callback();
} ).fail( function() {
batch.forEach( function(p) { paginaCache[p] = false; } );
pending--;
if ( pending === 0 ) callback();
} );
} );
}
// Descrizioni dalla pagina disambigua
function caricaDescrizioni( titoloBase, callback ) {
if ( _descCache[ titoloBase ] !== undefined ) { callback( _descCache[ titoloBase ] ); return; }
$.getJSON( API, {
action: 'query', prop: 'revisions', titles: titoloBase,
rvprop: 'content', rvslots: 'main', format: 'json'
}, function( data ) {
var desc = {};
try {
var page = data.query.pages[ Object.keys( data.query.pages )[0] ];
if ( page.revisions ) {
var wt = page.revisions[0].slots.main['*'];
wt.split('\n').forEach( function( riga ) {
var mm = riga.match( /\[\[([^\]|]+)\|[^\]]*\]\]'''?,\s*(.+)/ ) ||
riga.match( /\[\[([^\]]+\(\d+\))\]\]'''?,\s*(.+)/ );
if ( mm ) desc[ mm[1].trim() ] = mm[2].replace( /'''|''|;.*/g, '' ).trim();
} );
}
} catch(e) {}
_descCache[ titoloBase ] = desc;
callback( desc );
} ).fail( function() { _descCache[ titoloBase ] = {}; callback( {} ); } );
}
/* ── PANNELLO CANDIDATI LINK ───────────────────────────────
Approccio semplice e stabile: pannello sotto la toolbar
con chips cliccabili. Nessun overlay sulla textarea.
Cliccando una chip: posiziona il cursore sulla parola nel testo
e apre il popup link/ignora.
──────────────────────────────────────────────────────── */
var $hlPanelGlobale = null; // usato solo per il pannello gestione nonLink (globale)
var paroleTrovate = []; // per compatibilità con mostraGestioneNonLink
// Trova la posizione della prima occorrenza di una parola nel testo
// fuori da [[ ]] e {{ }}
function trovaPosizione( testo, parola, daPos ) {
var escaped = parola.replace( /[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&' );
var re = new RegExp( '\\b' + escaped + '\\b', 'gi' );
re.lastIndex = daPos || 0;
var m;
while ( ( m = re.exec( testo ) ) !== null ) {
var pos = m.index;
var primoLink = testo.lastIndexOf( '[[', pos );
if ( primoLink !== -1 ) {
var chiusuraLink = testo.indexOf( ']]', primoLink );
if ( chiusuraLink !== -1 && chiusuraLink > pos ) {
continue;
}
}
return pos;
}
return -1;
}
// Posizioni saltate con "Ignora questa volta" — si azzera ad ogni aggiornaHL
var posizioniSaltate = {};
// Estrae un frammento di contesto di ~30 caratteri attorno alla posizione
function estraiFrammento( testo, pos, parola ) {
var FINESTRA = 25;
var inizio = Math.max( 0, pos - FINESTRA );
var fine = Math.min( testo.length, pos + parola.length + FINESTRA );
return testo.substring( inizio, fine ).replace( /\s+/g, ' ' ).trim();
}
// Aggiunge un frammento come occorrenza saltata per questa pagina
function aggiungiOccorrenzaSaltata( parola, frammento ) {
if ( !nonLinkOccorrenze[ parola ] ) nonLinkOccorrenze[ parola ] = [];
if ( nonLinkOccorrenze[ parola ].indexOf( frammento ) === -1 ) {
nonLinkOccorrenze[ parola ].push( frammento );
}
alSalva();
}
// Verifica se una posizione nel testo corrisponde a un frammento già saltato
function isOccorrenzaSaltata( testo, pos, parola ) {
var saltate = nonLinkOccorrenze[ parola ];
if ( !saltate || !saltate.length ) return false;
var frammento = estraiFrammento( testo, pos, parola );
return saltate.indexOf( frammento ) !== -1;
}
// Trova la prossima occorrenza libera che non sia stata saltata
function trovaPosizioneLiberaSuccessiva( testo, parola, daPos ) {
var escaped = parola.replace( /[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&' );
var re = new RegExp( '\\b' + escaped + '\\b', 'gi' );
re.lastIndex = ( daPos !== -1 ? daPos + parola.length : 0 );
var saltateInMemoria = posizioniSaltate[ parola ] || [];
var m;
while ( ( m = re.exec( testo ) ) !== null ) {
var pos = m.index;
// Salta se dentro un link
var primoLink = testo.lastIndexOf( '[[', pos );
if ( primoLink !== -1 ) {
var chiusuraLink = testo.indexOf( ']]', primoLink );
if ( chiusuraLink !== -1 && chiusuraLink > pos ) continue;
}
// Salta se già marcata in memoria questa sessione
if ( saltateInMemoria.indexOf( pos ) !== -1 ) continue;
// Salta se corrisponde a un frammento salvato permanentemente
if ( isOccorrenzaSaltata( testo, pos, parola ) ) continue;
return pos;
}
return -1;
}
// Renderizza le chips nel pannello
// Pannello gestione parole ignorate (aperto con tasto destro su 🔍)
function mostraGestioneNonLink( textarea ) {
var $ov = $( '#bib-gestione-popup' );
if ( !$ov.length ) {
$ov = $( '<div id="bib-gestione-popup">' ).css( {
position: 'fixed', top: '50%', left: '50%',
transform: 'translate(-50%,-50%)',
background: '#fff', border: '1px solid #ccc',
'border-radius': '8px', padding: '16px',
'min-width': '380px', 'max-width': '560px',
'max-height': '80vh', 'overflow-y': 'auto',
'z-index': 100002, 'box-shadow': '0 4px 20px rgba(0,0,0,0.25)',
'font-family': 'sans-serif', 'font-size': '13px'
} );
$( 'body' ).append( $ov );
}
function rebuild() {
$ov.empty();
var $hdr = $( '<div>' ).css( { display: 'flex', 'justify-content': 'space-between', 'align-items': 'center', 'margin-bottom': '12px' } );
$hdr.append(
$( '<strong>' ).text( 'Gestione parole ignorate' ),
$( '<button type="button">' ).text( 'x' ).css( { background: 'none', border: 'none', cursor: 'pointer', 'font-size': '18px' } )
.on( 'click', function() { $ov.hide(); } )
);
$ov.append( $hdr );
function sezione( titolo, dizionario, fnRimuovi ) {
var chiavi = Object.keys( dizionario ).sort();
var $sec = $( '<div>' ).css( { 'margin-bottom': '14px' } );
$sec.append( $( '<div>' ).text( titolo + ' (' + chiavi.length + ')' )
.css( { 'font-weight': 'bold', 'margin-bottom': '6px', color: '#555', 'font-size': '12px', 'text-transform': 'uppercase', 'letter-spacing': '0.5px' } ) );
if ( !chiavi.length ) {
$sec.append( $( '<em>' ).text( 'nessuna parola' ).css( { color: '#bbb' } ) );
} else {
var $wrap = $( '<div>' ).css( { display: 'flex', 'flex-wrap': 'wrap', gap: '4px' } );
chiavi.forEach( function( parola ) {
var $chip = $( '<span>' ).css( {
display: 'inline-flex', 'align-items': 'center', gap: '4px',
padding: '2px 8px', background: '#f0f0f0',
border: '1px solid #ccc', 'border-radius': '10px',
'font-size': '12px', cursor: 'default'
} );
$chip.append( $( '<span>' ).text( parola ) );
$chip.append(
$( '<span>' ).text( 'x' ).css( { cursor: 'pointer', color: '#c00', 'font-weight': 'bold', 'font-size': '11px' } )
.on( 'click', function() {
fnRimuovi( parola );
rebuild(); // ricostruisce il pannello
aggiornaHL( textarea ); // rianalizza
} )
);
$wrap.append( $chip );
} );
$sec.append( $wrap );
}
$ov.append( $sec );
}
sezione( 'Ignorate in questa pagina', nonLinkPag, rimuoviNonLinkPag );
$ov.append( $( '<hr>' ).css( { border: 'none', 'border-top': '1px solid #eee', margin: '8px 0' } ) );
sezione( 'Ignorate in tutto il namespace', alData.nonLink, rimuoviNonLink );
$ov.show();
setTimeout( function() {
$( document ).one( 'click.bibgestione', function(e) {
if ( !$( e.target ).closest( '#bib-gestione-popup' ).length ) $ov.hide();
} );
}, 100 );
}
rebuild();
}
function mostraPopupHL( parola, textarea, $chip, $hlPanel, paroleTrovate ) {
function renderPopup( items, desc, suggeritoDaContesto, confidenza ) {
var $ov = $( '#bib-hl-popup' );
if ( !$ov.length ) {
$ov = $( '<div id="bib-hl-popup">' ).css( {
position: 'fixed', top: '80px', right: '16px',
background: '#fff', border: '1px solid #ccc',
'border-radius': '8px', padding: '16px',
'min-width': '320px', 'max-width': '420px',
'z-index': 100001, 'box-shadow': '0 4px 20px rgba(0,0,0,0.25)',
'font-family': 'sans-serif', 'font-size': '14px'
} );
$( 'body' ).append( $ov );
}
$ov.empty().show();
// Header con toggle autolink
var $hdr = $( '<div>' ).css( { display: 'flex', 'justify-content': 'space-between', 'align-items': 'center', 'margin-bottom': '6px' } );
var isAuto = alIsAutolink( parola );
var $autoBtn = $( '<button type="button">' )
.text( isAuto ? '⚡ Auto: ON' : '⚡ Auto: OFF' )
.css( {
background: isAuto ? '#d4f0d4' : '#f0f0f0',
border: '1px solid ' + ( isAuto ? '#4caf50' : '#ccc' ),
'border-radius': '4px', padding: '2px 8px',
cursor: 'pointer', 'font-size': '11px'
} )
.on( 'click', function() {
var nuovoStato = alToggleAutolink( parola );
$( this ).text( nuovoStato ? '⚡ Auto: ON' : '⚡ Auto: OFF' )
.css( { background: nuovoStato ? '#d4f0d4' : '#f0f0f0', border: '1px solid ' + ( nuovoStato ? '#4caf50' : '#ccc' ) } );
// Aggiorna la chip
renderChips( paroleTrovate, textarea, $hlPanel, paroleTrovate );
} );
$hdr.append(
$( '<strong>' ).text( 'Collega: "' + parola + '"' ),
$( '<div>' ).css( { display: 'flex', gap: '6px', 'align-items': 'center' } ).append(
$autoBtn,
$( '<button type="button">' ).text( '✕' ).css( { background: 'none', border: 'none', cursor: 'pointer', 'font-size': '20px' } )
.on( 'click', function() { $ov.hide(); } )
)
);
$ov.append( $hdr );
// Eventuale suggerimento contestuale
if ( suggeritoDaContesto ) {
var colore = confidenza === 'claude' ? '#e8f0fe' : ( confidenza >= 0.7 ? '#e8f5e9' : '#fff8e1' );
var etichetta = confidenza === 'claude' ? '🤖 Claude suggerisce' : ( confidenza >= 0.7 ? '✓ Suggerimento' : '? Suggerimento (bassa confidenza)' );
var $sug = $( '<div>' ).css( { background: colore, border: '1px solid #ccc', 'border-radius': '4px', padding: '5px 8px', 'margin-bottom': '6px', 'font-size': '12px' } );
$sug.append(
$( '<span>' ).text( etichetta + ': ' ).css( { color: '#555' } ),
$( '<strong>' ).text( suggeritoDaContesto ).css( { cursor: 'pointer', color: '#1a73e8' } )
.on( 'click', function() {
var pos = trovaPosizione( textarea.value, parola );
var paroleChiave = alEstraiContesto( textarea.value, pos !== -1 ? pos : 0, parola );
alRegistraContesto( parola, paroleChiave, suggeritoDaContesto );
alRegistraConferma( parola );
inserisciLinkNelTesto( suggeritoDaContesto, parola, textarea );
$ov.hide();
clearTimeout( hlTimer );
hlTimer = setTimeout( function() { aggiornaHL( textarea, $hlPanel, paroleTrovate ); }, 400 );
} )
);
$ov.append( $sug );
}
var $lista = $( '<div>' ).css( { 'max-height': '260px', 'overflow-y': 'auto', 'margin-bottom': '10px' } );
$ov.append( $lista );
// Tooltip anteprima pagina
var $tooltip = $( '#bib-hl-tooltip' );
if ( !$tooltip.length ) {
$tooltip = $( '<div id="bib-hl-tooltip">' ).css( {
position: 'fixed', background: '#fff',
border: '1px solid #ccc', 'border-radius': '6px',
padding: '10px 12px', 'width': '340px',
'max-height': '420px', 'overflow-y': 'auto',
'font-size': '12px', 'line-height': '1.6',
'z-index': 100002, 'box-shadow': '0 2px 12px rgba(0,0,0,0.2)',
display: 'none', 'pointer-events': 'none'
} );
$( 'body' ).append( $tooltip );
}
var _tooltipTimer = null;
var _estrattiCache = {};
function mostraTooltip( titolo, $el ) {
function renderTooltip( estratto ) {
$tooltip.empty();
$tooltip.append( $( '<strong>' ).text( titolo ).css( { display: 'block', 'margin-bottom': '5px', 'font-size': '13px' } ) );
$tooltip.append( $( '<div>' ).html( estratto ) );
// Posiziona a sinistra del popup
var ovOffset = $ov.offset();
var tooltipLeft = ( ovOffset ? ovOffset.left : 400 ) - 320;
var elOffset = $el.offset();
var tooltipTop = elOffset ? elOffset.top : 100;
$tooltip.css( { left: Math.max( 4, tooltipLeft ) + 'px', top: tooltipTop + 'px' } ).show();
}
if ( _estrattiCache[ titolo ] !== undefined ) {
if ( _estrattiCache[ titolo ] ) renderTooltip( _estrattiCache[ titolo ] );
return;
}
$.getJSON( API, {
action: 'query', prop: 'extracts',
titles: titolo, exintro: true,
exchars: 1500, explaintext: false,
format: 'json'
} ).done( function( data ) {
var pages = data.query && data.query.pages || {};
var page = pages[ Object.keys(pages)[0] ];
var estratto = page && page.extract ? page.extract : null;
_estrattiCache[ titolo ] = estratto;
if ( estratto ) renderTooltip( estratto );
} ).fail( function() {
_estrattiCache[ titolo ] = null;
} );
}
if ( !items.length ) {
$lista.append( $( '<div>' ).text( 'Nessuna pagina trovata.' ).css( { color: '#888', padding: '6px' } ) );
} else {
items.forEach( function( item ) {
var titolo = typeof item === 'string' ? item : item.titolo;
var esiste = typeof item === 'string' ? true : item.esiste;
var isSuggerito = titolo === suggeritoDaContesto;
var bg = esiste ? ( isSuggerito ? '#e8f5e9' : '#f5f5f5' ) : '#fff5f5';
var $it = $( '<div>' ).css( { padding: '7px 10px', cursor: 'pointer', 'border-radius': '4px', 'margin-bottom': '2px', background: bg } );
$it.append( $( '<span>' ).text( titolo ).css( { 'font-weight': 'bold', color: esiste ? '#000' : '#c00' } ) );
if ( desc && desc[ titolo ] ) {
$it.append( $( '<span>' ).text( ' — ' + desc[ titolo ] ).css( { color: '#555', 'font-size': '12px' } ) );
}
if ( !esiste ) {
$it.append( $( '<span>' ).text( ' (non ancora creata)' ).css( { color: '#c00', 'font-size': '11px' } ) );
}
$it.on( 'mouseenter', function() {
$( this ).css( 'background', esiste ? '#dceeff' : '#ffe0e0' );
if ( esiste ) {
clearTimeout( _tooltipTimer );
var $self = $( this );
_tooltipTimer = setTimeout( function() { mostraTooltip( titolo, $self ); }, 500 );
}
} )
.on( 'mouseleave', function() {
$( this ).css( 'background', bg );
clearTimeout( _tooltipTimer );
$tooltip.hide();
} )
.on( 'click', function() {
clearTimeout( _tooltipTimer );
$tooltip.hide();
var pos = trovaPosizione( textarea.value, parola );
var paroleChiave = alEstraiContesto( textarea.value, pos !== -1 ? pos : 0, parola );
alRegistraContesto( parola, paroleChiave, titolo );
alRegistraConferma( parola );
inserisciLinkNelTesto( titolo, parola, textarea );
$ov.hide();
clearTimeout( hlTimer );
hlTimer = setTimeout( function() { aggiornaHL( textarea, $hlPanel, paroleTrovate ); }, 400 );
} );
$lista.append( $it );
} );
}
var btnCss = {
width: '100%', padding: '7px', 'border-radius': '4px',
cursor: 'pointer', 'font-size': '12px', 'margin-top': '4px',
border: '1px solid #ccc', display: 'block'
};
function rimuoviChipEChiudi() {
$chip.remove();
$ov.hide();
$hlPanel.find( '.bib-count' ).text(
Math.max( 0, parseInt( $hlPanel.find( '.bib-count' ).text() || 0 ) - 1 )
);
}
var $btnPag = $( '<button type="button">' )
.text( 'Non è un link in questa pagina' )
.css( $.extend( {}, btnCss, { background: '#fff8e1' } ) )
.on( 'click', function() {
aggiungiNonLinkPag( parola );
rimuoviChipEChiudi();
} );
var $btnSempre = $( '<button type="button">' )
.text( 'Non è un link — ignora sempre (tutte le pagine)' )
.css( $.extend( {}, btnCss, { background: '#f0f0f0' } ) )
.on( 'click', function() {
aggiungiNonLink( parola );
rimuoviChipEChiudi();
} );
// Pulsante: salta questa occorrenza e vai alla prossima
var $btnSalta = $( '<button type="button">' )
.text( '→ Ignora questa volta (vai alla prossima)' )
.css( $.extend( {}, btnCss, { background: '#f0f8ff', 'border-color': '#99c' } ) )
.on( 'click', function() {
$ov.hide();
var val = textarea.value;
var posCorrente = trovaPosizione( val, parola );
if ( posCorrente !== -1 ) {
// Salva il frammento permanentemente
var frammento = estraiFrammento( val, posCorrente, parola );
aggiungiOccorrenzaSaltata( parola, frammento );
// Marca anche in memoria per questa sessione
if ( !posizioniSaltate[ parola ] ) posizioniSaltate[ parola ] = [];
posizioniSaltate[ parola ].push( posCorrente );
}
var posProssima = trovaPosizioneLiberaSuccessiva( val, parola, posCorrente );
if ( posProssima !== -1 ) {
textarea.focus();
textarea.setSelectionRange( posProssima, posProssima + parola.length );
var lineH = parseInt( $( textarea ).css( 'line-height' ) ) || 18;
var rigaPrima = val.substring( 0, posProssima ).split( '\n' ).length - 1;
textarea.scrollTop = Math.max( 0, rigaPrima * lineH - textarea.clientHeight / 3 );
mostraPopupHL( parola, textarea, $chip, $hlPanel, paroleTrovate );
} else {
$chip.remove();
$hlPanel.find( '.bib-count' ).text(
Math.max( 0, parseInt( $hlPanel.find( '.bib-count' ).text() || 0 ) - 1 )
);
}
} );
// Campo inserimento manuale
$ov.append( $( '<hr>' ).css( { border: 'none', 'border-top': '1px solid #eee', margin: '8px 0 4px' } ) );
var $titoloManuale = $( '<div>' ).text( 'Collega a una pagina specifica:' )
.css( { 'font-size': '11px', color: '#888', 'margin-bottom': '4px' } );
var $rigaManuale = $( '<div>' ).css( { display: 'flex', gap: '4px', 'margin-bottom': '6px' } );
var $inputManuale = $( '<input type="text">' )
.attr( 'placeholder', 'Es. Krishna (3)' )
.css( { flex: '1', padding: '5px 8px', border: '1px solid #ccc', 'border-radius': '4px', 'font-size': '13px' } );
var $btnManuale = $( '<button type="button">' ).text( '→ Inserisci' )
.css( { padding: '5px 10px', background: '#e8f0fe', border: '1px solid #aac', 'border-radius': '4px', cursor: 'pointer', 'font-size': '12px', 'white-space': 'nowrap' } );
$btnManuale.on( 'click', function() {
var titoloScelto = $inputManuale.val().trim();
if ( !titoloScelto ) return;
var pos = trovaPosizione( textarea.value, parola );
var paroleChiave = alEstraiContesto( textarea.value, pos !== -1 ? pos : 0, parola );
alRegistraContesto( parola, paroleChiave, titoloScelto );
alRegistraConferma( parola );
inserisciLinkNelTesto( titoloScelto, parola, textarea );
$ov.hide();
clearTimeout( hlTimer );
hlTimer = setTimeout( function() { aggiornaHL( textarea, $hlPanel, paroleTrovate ); }, 400 );
} );
$inputManuale.on( 'keydown', function(e) {
if ( e.which === 13 ) { e.preventDefault(); $btnManuale.trigger( 'click' ); }
} );
$rigaManuale.append( $inputManuale, $btnManuale );
$ov.append( $titoloManuale, $rigaManuale );
$ov.append( $btnSalta, $btnPag, $btnSempre );
setTimeout( function() {
$( document ).one( 'click.bibhl', function(e) {
if ( !$( e.target ).closest( '#bib-hl-popup' ).length ) $ov.hide();
} );
}, 100 );
}
// ── Ricerca + disambiguazione contestuale ─────────────
function cercaConDisambigua( parola, callback ) {
$.getJSON( API, {
action: 'query', prop: 'revisions',
titles: parola,
rvprop: 'content', rvslots: 'main', format: 'json'
}, function( data ) {
var pages = data.query && data.query.pages || {};
var pageId = Object.keys( pages )[0];
var page = pages[ pageId ];
var paginaEsatta = page && pageId !== '-1' ? { titolo: page.title, esiste: true } : { titolo: parola, esiste: false };
var isDisambigua = false;
var descFromPage = {};
var itemsFromPage = [];
if ( page && page.revisions ) {
var wt = page.revisions[0].slots.main['*'];
var parolaEsc = parola.replace( /[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&' );
var reVoce = new RegExp( '\\[\\[(' + parolaEsc + '\\s*\\(\\d+\\))[^\\]]*\\]\\][^\\n]*', 'gi' );
var mm;
while ( ( mm = reVoce.exec( wt ) ) !== null ) {
isDisambigua = true;
var titoloVoce = mm[1].trim();
itemsFromPage.push( { titolo: titoloVoce, esiste: true } );
var rigaCompleta = mm[0];
var descMatch = rigaCompleta.match( /\]\](?:\|[^\]]+\]\])?\s*[,;]?\s*(?:''')?(.+)/ );
if ( descMatch ) {
descFromPage[ titoloVoce ] = descMatch[1].replace( /'''|''|\[\[[^\]]*\]\]/g, '' ).trim().substring( 0, 80 );
}
}
}
if ( isDisambigua && itemsFromPage.length ) {
_descCache[ parola ] = descFromPage;
callback( itemsFromPage, descFromPage );
} else {
$.getJSON( API, {
action: 'query', list: 'search',
srsearch: parola, srnamespace: '*',
srlimit: 10, format: 'json'
}, function( data2 ) {
var itemsSearch = ( data2.query && data2.query.search || [] ).map( function(p) {
return { titolo: p.title, esiste: true };
} );
var titoli = {};
titoli[ paginaEsatta.titolo ] = true;
var altri = itemsSearch.filter( function(it) {
if ( titoli[ it.titolo ] ) return false;
titoli[ it.titolo ] = true;
return true;
} );
callback( [ paginaEsatta ].concat( altri ), {} );
} ).fail( function() { callback( [ paginaEsatta ], {} ); } );
}
} ).fail( function() { renderPopup( [], {}, null, null ); } );
}
cercaConDisambigua( parola, function( items, desc ) {
// Calcola contesto attuale per la disambiguazione
var pos = trovaPosizione( textarea.value, parola );
var paroleChiaveAttuali = alEstraiContesto( textarea.value, pos !== -1 ? pos : 0, parola );
// Prova prima il suggerimento da contesto locale
var sugLocale = alSuggerisciDaContesto( parola, paroleChiaveAttuali );
if ( sugLocale && sugLocale.confidenza >= AUTOLINK_SOGLIA_CONTESTO ) {
// Confidenza sufficiente → mostra subito con suggerimento
renderPopup( items, desc, sugLocale.scelta, sugLocale.confidenza );
} else if ( items.length > 1 && alData.contesti[ parola ] ) {
// Confidenza bassa ma abbiamo già dati contestuali → chiedi a Claude
var contestoTestuale = ( function() {
var FINESTRA = 400;
var prima = textarea.value.substring( Math.max(0, pos - FINESTRA), pos );
var dopo = textarea.value.substring( pos + parola.length, pos + parola.length + FINESTRA );
return ( prima + ' ··· ' + dopo ).replace( /\{\{[^}]*\}\}|\[\[|\]\]|<[^>]+>/g, ' ' ).trim();
}() );
var opzioniPerClaude = items.map( function(it) {
var t = typeof it === 'string' ? it : it.titolo;
return { titolo: t, desc: desc[t] || '' };
} );
// Mostra subito il popup (senza suggerimento) e poi aggiorna se Claude risponde
renderPopup( items, desc, null, null );
alChiediClaude( parola, contestoTestuale, opzioniPerClaude, function( suggerimento ) {
if ( suggerimento !== 'INCERTO' ) {
renderPopup( items, desc, suggerimento, 'claude' );
}
} );
} else {
// Nessun dato contestuale: mostra popup normale
renderPopup( items, desc, null, null );
}
} );
}
function inserisciLinkNelTesto( titolo, testoVisibile, textarea ) {
var val = textarea.value;
// Usa trovaPosizione per trovare la prima occorrenza LIBERA (fuori dai link)
var pos = trovaPosizione( val, testoVisibile );
if ( pos !== -1 ) {
var link = titolo === testoVisibile ? '[[' + titolo + ']]' : '[[' + titolo + '|' + testoVisibile + ']]';
textarea.value = val.substring( 0, pos ) + link + val.substring( pos + testoVisibile.length );
$( textarea ).trigger( 'input' );
textarea.focus();
}
}
function renderChips( trovate, textarea, $hlPanel, paroleTrovate ) {
if ( !$hlPanel ) return;
var $area = $hlPanel.find( '.bib-chips-area' );
$area.find( '.bib-chips-wrap' ).remove();
$area.find( '.bib-stato' ).text( '' );
$hlPanel.find( '.bib-count' ).text( trovate.length );
if ( !trovate.length ) {
$area.find( '.bib-stato' ).text( 'nessuna parola trovata' );
return;
}
var $wrap = $( '<span class="bib-chips-wrap">' ).css( { display: 'contents' } );
trovate.forEach( function( parola ) {
var isAuto = alIsAutolink( parola );
var bgNorm = isAuto ? '#d4f0d4' : '#fffbcc';
var bgHov = isAuto ? '#a8dfa8' : '#ffe066';
var bdCol = isAuto ? '#4caf50' : '#e6b800';
var $chip = $( '<span>' ).text( parola + ( isAuto ? ' ⚡' : '' ) ).css( {
display: 'inline-block', margin: '1px 2px', padding: '0px 7px',
background: bgNorm, border: '1px solid ' + bdCol,
'border-radius': '10px', cursor: 'pointer',
'font-size': '12px', 'font-family': 'sans-serif',
'line-height': '1.8', 'white-space': 'nowrap'
} );
$chip.on( 'mouseenter', function() { $( this ).css( 'background', bgHov ); } )
.on( 'mouseleave', function() { $( this ).css( 'background', bgNorm ); } )
.on( 'click', function() {
if ( alIsAutolink( parola ) ) {
inserisciLinkNelTesto( parola, parola, textarea );
alRegistraConferma( parola );
$chip.remove();
$hlPanel.find( '.bib-count' ).text(
Math.max( 0, parseInt( $hlPanel.find( '.bib-count' ).text() || 0 ) - 1 )
);
clearTimeout( hlTimer );
hlTimer = setTimeout( function() { aggiornaHL( textarea, $hlPanel, paroleTrovate ); }, 400 );
return;
}
// Trova la prima occorrenza libera non saltata
var val = textarea.value;
var pos = trovaPosizioneLiberaSuccessiva( val, parola, -1 );
if ( pos !== -1 ) {
textarea.focus();
textarea.setSelectionRange( pos, pos + parola.length );
var lineH = parseInt( $( textarea ).css( 'line-height' ) ) || 18;
var rigaPrima = val.substring( 0, pos ).split( '\n' ).length - 1;
textarea.scrollTop = Math.max( 0, rigaPrima * lineH - textarea.clientHeight / 3 );
}
mostraPopupHL( parola, textarea, $chip, $hlPanel, paroleTrovate );
} );
$wrap.append( $chip );
} );
$area.append( $wrap );
}
// Analisi principale
function aggiornaHL( textarea, $hlPanel, paroleTrovate ) {
posizioniSaltate = {}; // azzera le posizioni saltate ad ogni rianalisi
alCarica( function() {
// Aggiorna alias dopo caricamento
nonLinkPag = alData.nonLinkPag[ paginaCorrente ] || {};
var testo = textarea.value;
autoCleanNonLink( testo );
var tutti = estraiCandidati( testo );
if ( $hlPanel ) {
$hlPanel.find( '.bib-chips-area .bib-chips-wrap' ).remove();
$hlPanel.find( '.bib-stato' ).text( tutti.length ? '⏳ analisi…' : '' );
$hlPanel.find( '.bib-count' ).text( '?' );
}
if ( !tutti.length ) return;
var maiuscole = tutti.filter( function(p) { return /^[A-ZÀÈÉÌÒÙÁÉÍÓÚ]/.test( p ); } );
var minuscole = tutti.filter( function(p) { return /^[a-zàèéìòùáéíóú]/.test( p ); } );
function sortAlfa( arr ) {
return arr.slice().sort( function(a,b) { return a.localeCompare(b,'it',{sensitivity:'base'}); } );
}
// Carica le eccezioni (prima chiamata → API, poi cache)
caricaEccezioni( function() {
// Eccezioni: presenti nel testo e nell'elenco → sempre candidati
var minuscoleEccezione = minuscole.filter( function(p) {
return eccezioniMinuscole[ p ] &&
!alData.nonLink[ p ] && !nonLinkPag[ p ];
} );
// Restanti: richiedono verifica esistenza pagina
var minuscoleNormali = minuscole.filter( function(p) {
return !eccezioniMinuscole[ p ];
} );
if ( !minuscoleNormali.length ) {
paroleTrovate.length = 0;
sortAlfa( maiuscole.concat( minuscoleEccezione ) ).forEach( function(p) { paroleTrovate.push(p); } );
if ( $hlPanel ) $hlPanel.find( '.bib-stato' ).text( '' );
renderChips( paroleTrovate, textarea, $hlPanel, paroleTrovate );
return;
}
verificaEsistenza( minuscoleNormali, function() {
var minuscoleEsistenti = minuscoleNormali.filter( function(p) { return paginaCache[p] === true; } );
var trovati = sortAlfa( maiuscole.concat( minuscoleEccezione ).concat( minuscoleEsistenti ) );
paroleTrovate.length = 0;
trovati.forEach( function(p) { paroleTrovate.push(p); } );
if ( $hlPanel ) $hlPanel.find( '.bib-stato' ).text( '' );
renderChips( trovati, textarea, $hlPanel, paroleTrovate );
} );
} );
} );
}
/* ── 3. VERSIFICATORE ────────────────────────────────────
Due modalità:
A) AUTOMATICA — aggiunge {{v|C|N}} a ogni riga non versificata
(funziona se ogni verso è su una riga propria)
B) MANUALE — inserisce {{v|C|N}} alla posizione del cursore
(per testi continui, dove il verso non è su riga singola)
──────────────────────────────────────────────────────── */
// Calcola canto e prossimo numero verso dal testo corrente
function calcolaProgressivo( val, primaDelCursore ) {
var testo = primaDelCursore !== undefined ? primaDelCursore : val;
var cantoMatch = val.match( /\{\{v\|(\d+)\|/ );
var canto = cantoMatch ? parseInt( cantoMatch[1] ) : 1;
// Conta tutti i {{v|C|N}} già presenti PRIMA del cursore
var re = /\{\{v\|\d+\|(\d+)\}\}/g;
var m, ultimo = 0;
while ( ( m = re.exec( testo ) ) !== null ) { ultimo = parseInt( m[1] ); }
return { canto: canto, prossimo: ultimo + 1 };
}
// MODALITÀ A — per riga
function versificaRighe( textarea ) {
var val = textarea.value;
var p = calcolaProgressivo( val );
var canto = p.canto;
var nVerso = p.prossimo - 1; // partirà da prossimo
// Se ci sono già versificati, riparti da 0 e riconta
// (azzeriamo e ricostruiamo tutto in ordine)
nVerso = 0;
var linee = val.split( '\n' );
var nuove = linee.map( function( riga ) {
if ( riga.trim() === '' ) return riga;
if ( /^<\/?poem>/i.test( riga.trim() ) ) return riga;
if ( /^\{\{v\|/.test( riga.trim() ) ) return riga; // già versificata
if ( /^\[\[Categoria:/i.test( riga.trim() ) ) return riga; // categoria
if ( /^\{\{/.test( riga.trim() ) ) return riga; // template
nVerso++;
return '{{v|' + canto + '|' + nVerso + '}} ' + riga;
} );
textarea.value = nuove.join( '\n' );
$( textarea ).trigger( 'input' );
textarea.focus();
}
// Versifica solo la selezione (modalità A su blocco selezionato)
function versificaRigheSelezionate( textarea ) {
var start = textarea.selectionStart;
var end = textarea.selectionEnd;
if ( start === end ) { versificaRighe( textarea ); return; }
var val = textarea.value;
var prima = val.substring( 0, start );
var sel = val.substring( start, end );
var dopo = val.substring( end );
var p = calcolaProgressivo( val, prima );
var canto = p.canto;
var nVerso = p.prossimo - 1;
var linee = sel.split( '\n' ).map( function( riga ) {
if ( riga.trim() === '' ) return riga;
if ( /^<\/?poem>/i.test( riga.trim() ) ) return riga;
if ( /^\{\{v\|/.test( riga.trim() ) ) return riga;
if ( /^\[\[Categoria:/i.test( riga.trim() ) ) return riga;
if ( /^\{\{/.test( riga.trim() ) ) return riga;
nVerso++;
return '{{v|' + canto + '|' + nVerso + '}} ' + riga;
} );
var nuovaSel = linee.join( '\n' );
textarea.value = prima + nuovaSel + dopo;
textarea.selectionStart = start;
textarea.selectionEnd = start + nuovaSel.length;
$( textarea ).trigger( 'input' );
textarea.focus();
}
// MODALITÀ B — inserisce al cursore
function versificaAlCursore( textarea ) {
var val = textarea.value;
var pos = textarea.selectionStart;
var prima = val.substring( 0, pos );
var p = calcolaProgressivo( val, prima );
var token = '{{v|' + p.canto + '|' + p.prossimo + '}}';
textarea.value = prima + token + val.substring( pos );
textarea.selectionStart = textarea.selectionEnd = pos + token.length;
$( textarea ).trigger( 'input' );
textarea.focus();
}
// ── MODALITÀ TRAGEDIA ────────────────────────────────────
// Chiave localStorage per le parole escluse
var LS_KEY_TRAGEDIA = 'bib-tragedia-escluse';
function leggiParoleEscluse() {
try {
var raw = localStorage.getItem( LS_KEY_TRAGEDIA );
return raw ? JSON.parse( raw ) : [];
} catch(e) { return []; }
}
function salvaParoleEscluse( arr ) {
try { localStorage.setItem( LS_KEY_TRAGEDIA, JSON.stringify( arr ) ); } catch(e) {}
}
// Pulisce una riga: rimuove link [[...]], template {{...}}, : finale, spazi
function pulisciRigaTragedia( riga ) {
var t = riga;
t = t.replace( /\[\[[^\]]*\|([^\]]*)\]\]/g, '$1' ); // [[X|Y]] → Y
t = t.replace( /\[\[([^\]]*)\]\]/g, '$1' ); // [[X]] → X
t = t.replace( /\{\{[^}]*\}\}/g, '' ); // {{...}} → vuoto
t = t.replace( /'''?/g, '' ); // bold/italic
t = t.trim().replace( /:$/, '' ).trim(); // : finale
return t;
}
// Controlla se la riga pulita è "tutto maiuscolo" (nessuna lettera minuscola)
function isTuttoMaiuscolo( testo ) {
// Deve avere almeno una lettera e nessuna lettera minuscola
return /[A-ZÀÈÉÌÒÙÁÉÍÓÚÄ-Ü]/.test( testo ) && !/[a-zàèéìòùáéíóúä-ü]/.test( testo );
}
// Pattern per Strofe/Antistrofe/Epodo/Anastrofe + qualsiasi suffisso
var TRAGEDIA_STRUTTURA_RE = /^(Strofe|Antistrofe|Epodo|Anastrofe)/i;
// Verifica se una riga va saltata in modalità Tragedia
function iRigaSaltaTragedia( riga, paroleEscluse ) {
var trim = riga.trim();
// 1. Riga vuota
if ( trim === '' ) return true;
// 2. Riga che inizia con (
if ( trim.charAt(0) === '(' ) return true;
// 3. Già versificata / <poem> / categorie / template
if ( /^\{\{v\|/.test( trim ) ) return true;
if ( /^<\/?poem>/i.test( trim ) ) return true;
if ( /^\[\[Categoria:/i.test( trim ) ) return true;
if ( /^\{\{/.test( trim ) ) return true;
var pulita = pulisciRigaTragedia( trim );
// 4. Strofe|Antistrofe|Epodo|Anastrofe + suffisso
if ( TRAGEDIA_STRUTTURA_RE.test( pulita ) ) return true;
// 5. Tutto maiuscolo
if ( pulita.length > 0 && isTuttoMaiuscolo( pulita ) ) return true;
// 6. Parole escluse (confronto case-insensitive, parola intera o inizio riga)
for ( var i = 0; i < paroleEscluse.length; i++ ) {
var pe = paroleEscluse[i].trim();
if ( !pe ) continue;
// Controlla se la riga pulita inizia con la parola esclusa (case-insensitive)
if ( pulita.toLowerCase().indexOf( pe.toLowerCase() ) === 0 ) return true;
}
return false;
}
function versificaRigheTragedia( textarea ) {
var val = textarea.value;
var p = calcolaProgressivo( val );
var canto = p.canto;
var nVerso = 0;
var paroleEscluse = leggiParoleEscluse();
var linee = val.split( '\n' );
var nuove = linee.map( function( riga ) {
if ( iRigaSaltaTragedia( riga, paroleEscluse ) ) return riga;
nVerso++;
return '{{v|' + canto + '|' + nVerso + '}} ' + riga;
} );
textarea.value = nuove.join( '\n' );
$( textarea ).trigger( 'input' );
textarea.focus();
}
// Versifica solo la selezione in modalità Tragedia
function versificaRigheTragediaSelezionate( textarea ) {
var start = textarea.selectionStart;
var end = textarea.selectionEnd;
if ( start === end ) { versificaRigheTragedia( textarea ); return; }
var val = textarea.value;
var prima = val.substring( 0, start );
var sel = val.substring( start, end );
var dopo = val.substring( end );
var p = calcolaProgressivo( val, prima );
var nVerso = p.prossimo - 1;
var paroleEscluse = leggiParoleEscluse();
var linee = sel.split( '\n' ).map( function( riga ) {
if ( iRigaSaltaTragedia( riga, paroleEscluse ) ) return riga;
nVerso++;
return '{{v|' + p.canto + '|' + nVerso + '}} ' + riga;
} );
var nuovaSel = linee.join( '\n' );
textarea.value = prima + nuovaSel + dopo;
textarea.selectionStart = start;
textarea.selectionEnd = start + nuovaSel.length;
$( textarea ).trigger( 'input' );
textarea.focus();
}
// Pannello gestione parole escluse (modalità Tragedia)
var $tragediaPanel = null;
function mostraGestioneTragedia() {
if ( $tragediaPanel && $tragediaPanel.is( ':visible' ) ) {
$tragediaPanel.remove();
$tragediaPanel = null;
return;
}
if ( $tragediaPanel ) { $tragediaPanel.remove(); }
var parole = leggiParoleEscluse();
$tragediaPanel = $( '<div id="bib-tragedia-panel">' ).css( {
position: 'fixed', top: '80px', right: '20px',
background: '#fff', border: '1px solid #b0a0c0',
'border-radius': '8px', 'box-shadow': '0 4px 18px rgba(0,0,0,0.22)',
'z-index': 100010, 'font-family': 'sans-serif', 'font-size': '13px',
width: '300px', padding: '0'
} );
// Header
var $header = $( '<div>' ).css( {
background: '#6a3d8f', color: '#fff', 'border-radius': '8px 8px 0 0',
padding: '8px 12px', display: 'flex', 'align-items': 'center', gap: '8px'
} ).html( '<strong>🎭 Tragedia — Parole escluse</strong>' );
var $chiudi = $( '<button>' ).text( '✕' ).css( {
'margin-left': 'auto', background: 'none', border: 'none',
color: '#fff', cursor: 'pointer', 'font-size': '15px', padding: '0 2px'
} ).on( 'click', function() { $tragediaPanel.remove(); $tragediaPanel = null; } );
$header.append( $chiudi );
// Descrizione
var $desc = $( '<div>' ).css( {
padding: '8px 12px 4px', color: '#666', 'font-size': '11px',
'border-bottom': '1px solid #eee'
} ).text( 'Le righe che iniziano con queste parole verranno saltate durante la versificazione.' );
// Lista chips parole
var $lista = $( '<div id="bib-trag-lista">' ).css( {
padding: '8px 12px', 'min-height': '40px',
display: 'flex', 'flex-wrap': 'wrap', gap: '4px'
} );
function renderParole() {
$lista.empty();
var arr = leggiParoleEscluse();
if ( !arr.length ) {
$lista.append( $( '<em>' ).text( 'Nessuna parola esclusa' ).css( { color: '#bbb', 'font-size': '12px' } ) );
}
arr.forEach( function( p, idx ) {
var $chip = $( '<span>' ).css( {
background: '#ede0f8', color: '#5a2d82', border: '1px solid #c9a8e8',
'border-radius': '12px', padding: '2px 8px 2px 10px',
'font-size': '12px', cursor: 'default', display: 'inline-flex',
'align-items': 'center', gap: '4px'
} ).text( p );
var $x = $( '<span>' ).text( '×' ).css( {
cursor: 'pointer', color: '#9a4cc4', 'font-size': '14px',
'line-height': '1', 'margin-left': '2px'
} ).on( 'click', function() {
var arr2 = leggiParoleEscluse();
arr2.splice( idx, 1 );
salvaParoleEscluse( arr2 );
renderParole();
} );
$chip.append( $x );
$lista.append( $chip );
} );
}
renderParole();
// Input aggiunta
var $inputRow = $( '<div>' ).css( {
padding: '6px 12px 10px', display: 'flex', gap: '6px',
'border-top': '1px solid #eee'
} );
var $input = $( '<input type="text" placeholder="Aggiungi parola…">' ).css( {
flex: '1', padding: '4px 8px', border: '1px solid #ccc',
'border-radius': '4px', 'font-size': '13px'
} );
var $aggiungi = $( '<button>' ).text( '+ Aggiungi' ).css( {
background: '#6a3d8f', color: '#fff', border: 'none',
'border-radius': '4px', padding: '4px 10px',
cursor: 'pointer', 'font-size': '12px', 'white-space': 'nowrap'
} );
function aggiungiParola() {
var v = $input.val().trim();
if ( !v ) return;
var arr = leggiParoleEscluse();
if ( arr.indexOf(v) === -1 ) { arr.push(v); salvaParoleEscluse(arr); }
$input.val( '' );
renderParole();
}
$aggiungi.on( 'click', aggiungiParola );
$input.on( 'keydown', function(e) { if ( e.which === 13 ) { e.preventDefault(); aggiungiParola(); } } );
$inputRow.append( $input, $aggiungi );
// Pulsante "Cancella tutto"
var $footer = $( '<div>' ).css( {
padding: '0 12px 10px', 'text-align': 'right'
} );
var $reset = $( '<button>' ).text( 'Cancella tutto' ).css( {
background: 'none', border: 'none', color: '#c00',
cursor: 'pointer', 'font-size': '11px', 'text-decoration': 'underline'
} ).on( 'click', function() {
if ( confirm( 'Eliminare tutte le parole escluse?' ) ) {
salvaParoleEscluse( [] );
renderParole();
}
} );
$footer.append( $reset );
$tragediaPanel.append( $header, $desc, $lista, $inputRow, $footer );
$( 'body' ).append( $tragediaPanel );
$input.focus();
}
// Mini-menu a comparsa per scegliere la modalità
var $versMenu = null;
function mostraMenuVersifica( textarea, btnEl ) {
if ( $versMenu && $versMenu.is( ':visible' ) ) { $versMenu.hide(); return; }
if ( !$versMenu ) {
$versMenu = $( '<div id="bib-vers-menu">' ).css( {
position: 'absolute', background: '#fff', border: '1px solid #ccc',
'border-radius': '6px', 'box-shadow': '0 3px 12px rgba(0,0,0,0.18)',
'z-index': 100002, 'font-family': 'sans-serif', 'font-size': '13px',
'min-width': '280px', padding: '4px 0'
} );
$( 'body' ).append( $versMenu );
}
$versMenu.empty();
function voce( emoji, titolo, desc, fn ) {
var $v = $( '<div>' ).css( { padding: '9px 14px', cursor: 'pointer' } );
$v.append(
$( '<div>' ).html( emoji + ' <strong>' + titolo + '</strong>' ),
$( '<div>' ).text( desc ).css( { color: '#666', 'font-size': '11px', 'margin-top': '2px' } )
);
$v.on( 'mouseenter', function() { $(this).css('background','#f0f4ff'); } )
.on( 'mouseleave', function() { $(this).css('background',''); } )
.on( 'click', function() { $versMenu.hide(); fn(); } );
$versMenu.append( $v );
}
voce( '📜', 'Versifica righe (automatico)',
'Aggiunge {{v|C|N}} a ogni riga — funziona se ogni verso è su riga propria',
function() { versificaRigheSelezionate( textarea ); } );
$versMenu.append( $('<hr>').css({margin:'4px 0',border:'none','border-top':'1px solid #eee'}) );
voce( '🎭', 'Versifica — modalità Tragedia',
'Come automatico, ma salta intestazioni strutturali, didascalie, tutto-maiuscolo e parole escluse',
function() { versificaRigheTragediaSelezionate( textarea ); } );
// Link gestione parole escluse (visibile solo in fondo alla voce Tragedia)
var $linkEscluse = $( '<div>' ).css( {
padding: '3px 14px 8px', 'font-size': '11px', color: '#8a4cc4',
cursor: 'pointer', 'text-decoration': 'underline'
} ).text( '⚙ Gestisci parole escluse (Tragedia)' )
.on( 'click', function() {
$versMenu.hide();
mostraGestioneTragedia();
} );
$versMenu.append( $linkEscluse );
$versMenu.append( $('<hr>').css({margin:'4px 0',border:'none','border-top':'1px solid #eee'}) );
voce( '✍️', 'Inserisci numero verso al cursore (manuale)',
'Inserisce {{v|C|N}} dove si trova il cursore — per testi in prosa continua',
function() { versificaAlCursore( textarea ); } );
// Posiziona sotto il bottone
var off = $( btnEl ).offset();
var h = $( btnEl ).outerHeight();
$versMenu.css( { top: off.top + h + 4, left: off.left } ).show();
setTimeout( function() {
$( document ).one( 'click.versmenu', function(e) {
if ( !$( e.target ).closest( '#bib-vers-menu' ).length &&
!$( e.target ).is( '.bib-versifica-btn' ) ) {
$versMenu.hide();
}
} );
}, 100 );
}
/* ── INIT ────────────────────────────────────────────────*/
function initBibliotecaTools( $textarea ) {
var textarea = $textarea[0];
if ( $textarea.data( 'bib-init' ) ) return;
$textarea.data( 'bib-init', true );
var isBiblioteca = ( nsCorrente === NS_BIBLIOTECA );
// Pannello e lista parole LOCALI a questa textarea
var $hlPanel = null;
var paroleTrovate = [];
// --- Pulsanti nella toolbar ---
function aggiungiBotoni( tentativi ) {
tentativi = tentativi || 0;
var $wikiUI = $textarea.closest( '.wikiEditor-ui' );
if ( !$wikiUI.length && tentativi < 10 ) {
setTimeout( function() { aggiungiBotoni( tentativi + 1 ); }, 200 * (tentativi + 1) );
return;
}
var toolbar = $wikiUI.find( '.wikiEditor-ui-toolbar' )[0];
if ( !toolbar && tentativi < 10 ) {
setTimeout( function() { aggiungiBotoni( tentativi + 1 ); }, 200 * (tentativi + 1) );
return;
}
if ( toolbar && !toolbar.querySelector( '.bib-hl-btn' ) ) {
// Pulsante highlight — presente in tutti i namespace
var btnHL = document.createElement( 'button' );
btnHL.type = 'button';
btnHL.className = 'bib-hl-btn';
btnHL.textContent = '🔍';
btnHL.title = 'Aggiorna highlight parole candidate link';
btnHL.style.cssText = 'padding:2px 6px;cursor:pointer;font-size:15px;background:none;border:1px solid #ccc;border-radius:4px;margin:2px;vertical-align:middle;';
btnHL.addEventListener( 'click', function() { aggiornaHL( textarea, $hlPanel, paroleTrovate ); } );
btnHL.addEventListener( 'contextmenu', function(e) {
e.preventDefault();
mostraGestioneNonLink( textarea );
} );
toolbar.prepend( btnHL );
// Pulsante versificatore — solo in Biblioteca
if ( isBiblioteca && !toolbar.querySelector( '.bib-versifica-btn' ) ) {
var btnV = document.createElement( 'button' );
btnV.type = 'button';
btnV.className = 'bib-versifica-btn';
btnV.textContent = '📜';
btnV.title = 'Versifica — scegli modalità';
btnV.style.cssText = 'padding:2px 6px;cursor:pointer;font-size:15px;background:none;border:1px solid #ccc;border-radius:4px;margin:2px;vertical-align:middle;';
btnV.addEventListener( 'click', function(e) {
e.stopPropagation();
mostraMenuVersifica( textarea, btnV );
} );
toolbar.prepend( btnV );
}
}
}
aggiungiBotoni();
// --- Pannello chips candidati link (locale a questa textarea) ---
var $header = $( '<div>' ).css( {
display: 'flex', 'align-items': 'center', gap: '6px',
padding: '2px 8px', background: '#f0f0f0',
'border-bottom': '1px solid #ddd', cursor: 'pointer',
'font-size': '12px', 'user-select': 'none'
} );
var $labelLink = $( '<span>' ).text( 'Link candidati: ' ).css( { color: '#777' } );
var $labelCount = $( '<span class="bib-count">' ).text( '0' ).css( { color: '#333', 'font-weight': 'bold' } );
var $labelToggle = $( '<span class="bib-toggle">' ).text( '▼' ).css( { color: '#999', 'font-size': '10px', 'margin-left': 'auto' } );
$header.append( $labelLink, $labelCount, $( '<span>' ).text( ' parole' ).css( { color: '#999' } ), $labelToggle );
var $chipsArea = $( '<div class="bib-chips-area">' ).css( {
padding: '3px 8px 4px', background: '#fafafa',
'border-bottom': '1px solid #e0e0e0',
'max-height': '160px', 'overflow-y': 'auto',
display: 'flex', 'flex-wrap': 'wrap', gap: '2px',
'align-items': 'center', 'font-size': '12px', 'line-height': '1.8'
} );
$chipsArea.append( $( '<span class="bib-stato">' ).css( { color: '#bbb', 'font-style': 'italic' } ) );
var aperto = true;
$header.on( 'click', function() {
aperto = !aperto;
$chipsArea.toggle( aperto );
$labelToggle.text( aperto ? '▼' : '▶' );
} );
$hlPanel = $( '<div class="bib-hl-panel">' ).append( $header, $chipsArea );
var $wikiUI = $textarea.closest( '.wikiEditor-ui' );
var $toolbar = $wikiUI.find( '.wikiEditor-ui-toolbar' );
if ( $toolbar.length ) {
$toolbar.after( $hlPanel );
} else {
$textarea.before( $hlPanel );
}
// In Biblioteca adatta l'altezza della textarea
if ( isBiblioteca ) {
setTimeout( function() {
$textarea.height( 600 );
$textarea.closest( '.wikiEditor-ui-text' ).css( 'height', 'auto' );
}, 300 );
}
// Debounce analisi al cambio testo
$textarea.on( 'input.bibhl', function() {
clearTimeout( hlTimer );
hlTimer = setTimeout( function() { aggiornaHL( textarea, $hlPanel, paroleTrovate ); }, 1200 );
} );
// Prima analisi al caricamento
setTimeout( function() { aggiornaHL( textarea, $hlPanel, paroleTrovate ); }, 1500 );
}
// Hook WikiEditor (maschera + wikieditor standard)
mw.hook( 'wikiEditor.toolbarReady' ).add( function( $textarea ) {
initBibliotecaTools( $textarea );
} );
// Fallback: cerca la textarea principale dopo il DOM ready
$( function() {
setTimeout( function() {
$( '#wpTextbox1' ).each( function() {
initBibliotecaTools( $( this ) );
} );
}, 1000 );
} );
}( mw, jQuery ) );
/* =========================================================
DPL VERSO ANNOTATOR
Per ogni pagina Biblioteca in un elenco DPL, trova il verso
in cui compare il link alla voce corrente e lo mostra accanto.
========================================================= */
( function ( mw, $ ) {
'use strict';
// Si attiva solo su pagine normali (non edit)
if ( mw.config.get('wgAction') !== 'view' ) return;
var API = mw.util.wikiScript('api');
// Titolo della pagina corrente (es. "Medea")
var paginaCorrente = mw.config.get('wgPageName'); // es. "Medea" o "Medea_(disambigua)"
var titoloPulito = mw.config.get('wgTitle'); // senza namespace
$( function() {
// Trova tutti i contenitori DPL collapsible nella pagina
var $dplLinks = $( '.mw-collapsible-content a[href]' ).filter( function() {
// Supporta sia /wiki/Biblioteca: che ?title=Biblioteca:
return /Biblioteca:/.test( this.href );
} );
if ( !$dplLinks.length ) return;
// Aggiunge spinner accanto a ogni link
$dplLinks.each( function() {
$( this ).after(
$( '<span class="dpl-verso-loading">' )
.text( ' …' )
.css( { color: '#aaa', 'font-size': '11px' } )
);
} );
// Raccoglie titoli unici delle pagine Biblioteca da controllare
var titoliBiblioteca = [];
var linkMap = {}; // titolo → array di elementi <a>
$dplLinks.each( function() {
var $a = $( this );
// Estrai titolo dalla href: /wiki/Biblioteca:Titolo → "Biblioteca:Titolo"
var href = $a.attr('href');
var match = href.match( /[?&]title=(Biblioteca:[^&#]+)/ ) ||
href.match( /\/wiki\/(Biblioteca:[^#?]+)/ );
if ( !match ) return;
var titolo = decodeURIComponent( match[1].replace(/_/g, ' ') );
if ( !linkMap[ titolo ] ) {
linkMap[ titolo ] = [];
titoliBiblioteca.push( titolo );
}
linkMap[ titolo ].push( $a );
} );
// Costruisce regex per trovare il link alla pagina corrente nel wikitext
// Considera sia [[Titolo]] che [[Titolo|testo]] e [[Titolo (N)|testo]]
function buildLinkRegex( pagina ) {
// Escapa caratteri speciali nel titolo
var escaped = pagina.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
// Matcha [[Titolo...]] in tutte le varianti
return new RegExp( '\\[\\[' + escaped + '[\\]|]', 'i' );
}
// Trova TUTTI i versi in cui compare il link — restituisce array di {canto, verso}
// Cache wikitext per ogni titolo Biblioteca — usata dal tooltip contestuale
var wikitextCache = {};
// Rileva il tipo di struttura della pagina Biblioteca
// Tipo A: versi con {{v|C|N}} → anchor capitolo_C_versettaN
// Tipo B: sezioni con ===N=== → anchor #N
function trovaVersi( wikitext, linkRegex ) {
var righe = wikitext.split('\n');
var risultati = [];
var sezioneCorrente = null; // per pagine con ===N===
for ( var i = 0; i < righe.length; i++ ) {
// Aggiorna la sezione corrente se troviamo ===N===
var sezioneMatch = righe[i].match( /^===\s*(\d+)\s*===$/ );
if ( sezioneMatch ) {
sezioneCorrente = parseInt( sezioneMatch[1] );
}
linkRegex.lastIndex = 0;
if ( linkRegex.test( righe[i] ) ) {
var vm = righe[i].match( /\{\{v\|(\d+)\|(\d+)\}\}/ );
if ( vm ) {
// Tipo A: pagina con versi numerati
risultati.push( { tipo: 'verso', canto: parseInt(vm[1]), verso: parseInt(vm[2]), rigaIdx: i } );
} else if ( sezioneCorrente !== null ) {
// Tipo B: pagina con sezioni ===N===
risultati.push( { tipo: 'sezione', numero: sezioneCorrente, rigaIdx: i } );
} else {
risultati.push( { tipo: 'generico', rigaIdx: i } );
}
}
}
return risultati;
}
// Righe da scartare completamente (non sono testo della fonte)
function rigaDaScartare( riga ) {
var s = riga.trim();
if ( !s ) return true;
if ( /^={2,}/.test(s) ) return true; // ==Titolo==, ===Titolo===
if ( /^__[A-Z]+__/.test(s) ) return true; // __NOTOC__, __TOC__, ecc.
if ( /^\[\[Categoria:/i.test(s) ) return true; // [[Categoria:...]]
if ( /^\[\[Category:/i.test(s) ) return true; // [[Category:...]]
if ( /^\{\|/.test(s) ) return true; // inizio tabella {|
if ( /^\|[-}]/.test(s) ) return true; // |- o |} (tabella)
if ( /^!/.test(s) ) return true; // intestazione tabella
if ( /^<(references|ref|gallery|noinclude|includeonly)/i.test(s) ) return true;
if ( /^\{\{[Cc]at/.test(s) ) return true; // template categoria
if ( /^\{\{DEFAULTSORT/i.test(s) ) return true;
return false;
}
// Pulisce una riga wikitext: rimuove markup mantenendo solo il testo
function pulisciRiga( riga ) {
var s = riga
.replace( /\{\{v\|\d+\|\d+\}\}/g, '' ) // {{v|C|N}}
.replace( /<!--[\s\S]*?-->/g, '' ) // commenti HTML
.replace( /\{\{[Cc]hiarimento[^}]*\}\}/g, '' ) // template inline
.replace( /\{\{[Cc]itazione[^}]*\}\}/g, '' )
.replace( /\{\{[^|}][^}]*\}\}/g, '' ) // template semplici
.replace( /\[\[(?:[Ff]ile|[Ii]mage|[Ii]magine):[^\]]+\]\]/g, '' ) // immagini
.replace( /\[\[([^\]|]+)\|([^\]]+)\]\]/g, '$2' ) // [[link|testo]] → testo
.replace( /\[\[([^\]]+)\]\]/g, '$1' ) // [[link]] → link
.replace( /\[[^\]]*https?:[^\]]*\s([^\]]+)\]/g, '$1' ) // [url testo] → testo
.replace( /\[[^\]]*https?:[^\]]*\]/g, '' ) // [url] nudo
.replace( /'{3}([^']+)'{3}/g, '$1' ) // '''grassetto'''
.replace( /'{2}([^']+)'{2}/g, '$1' ) // ''corsivo''
.replace( /^[;:*#]+\s*/, '' ) // markup lista
.replace( /^\|\s*/, '' ) // cella tabella
.replace( /\s{2,}/g, ' ' ) // spazi multipli
.trim();
return s;
}
// Evidenzia il nome del personaggio nella riga focus (solo la prima occorrenza)
function evidenziaVoce( testo, nomePagina ) {
// Usa il titolo pulito (senza namespace e senza disambigua tra parentesi)
var nome = nomePagina.replace( /^[^:]+:/, '' ) // rimuovi namespace
.replace( /\s*\([^)]+\)$/, '' ) // rimuovi (disambigua)
.trim();
if ( !nome ) return $('<span>').text(testo).html();
// Cerca il nome (case-insensitive, parola intera)
var escaped = nome.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
var re = new RegExp( '(' + escaped + ')', 'i' );
var parts = testo.split( re );
var html = '';
for ( var k = 0; k < parts.length; k++ ) {
if ( re.test(parts[k]) ) {
html += '<strong style="color:#1a1a1a">' + $('<span>').text(parts[k]).html() + '</strong>';
} else {
html += $('<span>').text(parts[k]).html();
}
}
return html;
}
// Restituisce HTML con il contesto ±3 righe attorno a rigaIdx
function estrattoContestuale( titolo, rigaIdx ) {
var wt = wikitextCache[ titolo ];
if ( !wt ) return null;
var righe = wt.split('\n');
var INTORNO = 3;
var da = Math.max( 0, rigaIdx - INTORNO );
var a = Math.min( righe.length - 1, rigaIdx + INTORNO );
var html = '';
for ( var j = da; j <= a; j++ ) {
if ( rigaDaScartare( righe[j] ) ) continue;
var testo = pulisciRiga( righe[j] );
if ( !testo ) continue;
var cls = j === rigaIdx ? 'dpl-ctx-focus' : 'dpl-ctx-line';
var contenuto = j === rigaIdx
? evidenziaVoce( testo, titoloPulito )
: $('<span>').text(testo).html();
html += '<div class="' + cls + '">' + contenuto + '</div>';
}
return html || null;
}
// Aggiorna il DOM con i versi/sezioni trovati come link cliccabili
// Attacca data-titolo-bib e data-riga-idx per il tooltip contestuale
function applicaVersi( titoloBiblioteca, versi, paginaHref ) {
var $links = linkMap[ titoloBiblioteca ];
if ( !$links ) return;
$links.forEach( function( $a ) {
var $spinner = $a.next( '.dpl-verso-loading' );
var trovati = versi.filter( function(v) { return v !== null; } );
// Tooltip sul link principale (primo verso trovato)
if ( trovati.length ) {
$a.addClass('dpl-bib-link')
.attr('data-titolo-bib', titoloBiblioteca)
.attr('data-riga-idx', trovati[0].rigaIdx);
}
if ( !trovati.length ) {
$spinner.remove();
return;
}
// Se tutti i trovati sono 'generico' (nessun verso/sezione nota), non mostrare parentesi
var conEtichetta = trovati.filter( function(v) { return v.tipo !== 'generico'; } );
if ( !conEtichetta.length ) {
$spinner.remove();
return;
}
var $wrap = $( '<span>' ).css( { 'font-size': '13px', color: '#888' } );
$wrap.append( ' (' );
var firstShown = true;
trovati.forEach( function( v, idx ) {
var anchor, label;
if ( v.tipo === 'verso' ) {
anchor = 'capitolo_' + v.canto + '_versetto' + v.verso;
label = v.canto > 1 ? 'c.' + v.canto + ' v.' + v.verso : 'v.' + v.verso;
} else if ( v.tipo === 'sezione' ) {
anchor = v.numero;
label = '§' + v.numero;
} else {
// tipo 'generico': nessun verso noto, non mostrare etichetta numerica
return;
}
if ( !firstShown ) $wrap.append( ', ' );
firstShown = false;
var $vlink = $( '<a>' )
.attr( 'href', paginaHref + '#' + anchor )
.text( label )
.css( { color: '#888', 'text-decoration': 'underline dotted' } )
.addClass('dpl-bib-link')
.attr('data-titolo-bib', titoloBiblioteca)
.attr('data-riga-idx', v.rigaIdx);
$wrap.append( $vlink );
} );
$wrap.append( ')' );
$spinner.replaceWith( $wrap );
} );
}
// Processa le pagine in batch da 50
var linkRegex = buildLinkRegex( titoloPulito );
var batch_size = 50;
function processaBatch( offset ) {
var batch = titoliBiblioteca.slice( offset, offset + batch_size );
if ( !batch.length ) return;
$.getJSON( API, {
action: 'query',
prop: 'revisions',
titles: batch.join('|'),
rvprop: 'content',
rvslots: 'main',
format: 'json'
}, function( data ) {
var pages = ( data.query && data.query.pages ) || {};
Object.keys( pages ).forEach( function( id ) {
var page = pages[id];
if ( !page.revisions ) return;
var wikitext = page.revisions[0].slots.main['*'] || '';
wikitextCache[ page.title ] = wikitext; // cache per tooltip contestuale
var titolo = page.title;
// Costruisce href base della pagina Biblioteca
var paginaHref = '/index.php?title=' + encodeURIComponent( titolo.replace(/ /g,'_') );
var versi = trovaVersi( wikitext, linkRegex );
applicaVersi( titolo, versi, paginaHref );
} );
// Rimuovi spinner residui per pagine non trovate
batch.forEach( function(t) {
if ( linkMap[t] ) {
linkMap[t].forEach( function($a) {
$a.next('.dpl-verso-loading').remove();
});
}
});
// Prossimo batch con piccolo delay per non sovraccaricare l'API
setTimeout( function() { processaBatch( offset + batch_size ); }, 300 );
} ).fail( function() {
// In caso di errore rimuovi gli spinner
batch.forEach( function(t) {
if ( linkMap[t] ) {
linkMap[t].forEach( function($a) {
$a.next('.dpl-verso-loading').remove();
});
}
});
setTimeout( function() { processaBatch( offset + batch_size ); }, 300 );
});
}
processaBatch(0);
// -------------------------------------------------------
// TOOLTIP CONTESTUALE: hover su .dpl-bib-link
// Mostra 3 righe prima e 3 dopo la riga in cui compare
// il personaggio nel wikitext della fonte.
// -------------------------------------------------------
var $dplTooltip = $( '<div id="dpl-ctx-tooltip">' ).css( {
position: 'fixed',
background: '#fff',
border: '1px solid #b0b8c4',
'border-radius': '7px',
padding: '10px 14px',
'min-width': '280px',
'max-width': '420px',
'box-shadow': '0 4px 18px rgba(0,0,0,0.18)',
'font-size': '13px',
'line-height': '1.7',
'z-index': 99999,
display: 'none',
'pointer-events': 'none',
'font-family': 'Georgia, serif'
} ).appendTo( 'body' );
// CSS per le righe del tooltip
$( '<style>' ).text(
'#dpl-ctx-tooltip .dpl-ctx-focus {' +
'font-weight: bold; color: #1a1a1a;' +
'background: #fffbe6;' +
'border-left: 3px solid #c8a84b;' +
'padding-left: 6px; margin: 1px 0;' +
'}' +
'#dpl-ctx-tooltip .dpl-ctx-line {' +
'color: #555; margin: 1px 0;' +
'}' +
'#dpl-ctx-tooltip .dpl-ctx-header {' +
'font-size: 11px; color: #888;' +
'margin-bottom: 6px; font-style: italic;' +
'}'
).appendTo( 'head' );
var _ctxTimer = null;
$( document ).on( 'mouseenter', '.dpl-bib-link', function( e ) {
var $el = $( this );
var titolo = $el.attr('data-titolo-bib');
var rigaIdx = parseInt( $el.attr('data-riga-idx'), 10 );
if ( !titolo || isNaN( rigaIdx ) ) return;
clearTimeout( _ctxTimer );
_ctxTimer = setTimeout( function() {
var html = estrattoContestuale( titolo, rigaIdx );
if ( !html ) return;
var titoloBreve = titolo.replace( /^Biblioteca:/, '' );
$dplTooltip.empty()
.append( $('<div class="dpl-ctx-header">').text( titoloBreve ) )
.append( $('<div>').html( html ) );
// Posizionamento: preferisce a destra del cursore, poi a sinistra
var x = e.clientX + 14;
var y = e.clientY - 20;
var W = $dplTooltip.outerWidth(true) || 320;
if ( x + W > window.innerWidth - 10 ) {
x = e.clientX - W - 14;
}
if ( x < 6 ) x = 6;
if ( y + 200 > window.innerHeight ) {
y = window.innerHeight - 210;
}
$dplTooltip.css( { left: x + 'px', top: y + 'px' } ).show();
}, 350 );
} );
$( document ).on( 'mousemove', '.dpl-bib-link', function( e ) {
if ( $dplTooltip.is(':visible') ) {
var W = $dplTooltip.outerWidth(true) || 320;
var x = e.clientX + 14;
var y = e.clientY - 20;
if ( x + W > window.innerWidth - 10 ) x = e.clientX - W - 14;
if ( x < 6 ) x = 6;
if ( y + 200 > window.innerHeight ) y = window.innerHeight - 210;
$dplTooltip.css( { left: x + 'px', top: y + 'px' } );
}
} );
$( document ).on( 'mouseleave', '.dpl-bib-link', function() {
clearTimeout( _ctxTimer );
$dplTooltip.hide();
} );
} );
}( mw, jQuery ) );
/* ── Fix: nasconde solo i paragrafi REALMENTE vuoti nelle sezioni Citizen ──
Il CSS (MediaWiki:Common.css) usa .citizen-section > p.p-vuoto-br per
nascondere i paragrafi vuoti generati dalle righe vuote nel wikitesto.
Non può farlo con puro CSS perché :has(> br:only-child) guarda solo la
struttura (quanti elementi figli ha il <p>), non il testo: un paragrafo
con un solo <br> ma pieno di prosa (es. "Testo...<br>Altro testo...")
ha comunque un solo elemento figlio (il <br>), quindi risulterebbe
indistinguibile da un paragrafo vuoto tipo "<p><br></p>".
Qui invece controlliamo il textContent effettivo: solo se non c'è
davvero nessun testo (spazi esclusi) marchiamo il paragrafo come vuoto. */
( function () {
'use strict';
function marcaParagrafiVuotiCitizen() {
document.querySelectorAll( '.citizen-section > p' ).forEach( function ( p ) {
var vuoto = p.textContent.replace( /\s+/g, '' ) === '';
p.classList.toggle( 'p-vuoto-br', vuoto );
} );
}
mw.hook( 'wikipage.content' ).add( marcaParagrafiVuotiCitizen );
}() );
/* =========================================================
MAPPA INTERATTIVA (Leaflet)
Attiva solo sulla pagina "Mappa": carica Leaflet da CDN e
costruisce una mappa a drill-down (Paese → Regione →
Provincia → Città) più uno strato indipendente di Luoghi
puntuali (tabella Cargo "Luoghi"), a partire da:
- MediaWiki:geo.json (gerarchia dei nomi)
- MediaWiki:GeoConfiniIndex.json (poligoni/coordinate)
- tabella Cargo Luoghi (punti episodio-specifici)
I file dei confini (Natural Earth/ISTAT/geoBoundaries) sono
scaricati solo quando servono (al primo drill-down in
quell'area) e tenuti in cache per il resto della sessione.
========================================================= */
( function ( mw, $ ) {
'use strict';
if ( mw.config.get('wgPageName') !== 'Mappa' ) return;
if ( mw.config.get('wgAction') !== 'view' ) return;
var API = mw.util.wikiScript('api');
var REGIONE_FITTIZIA = '—'; // stessa convenzione di Tools.html
// Riconosce la "regione fittizia" tollerando qualunque trattino/lineetta
// (- trattino normale, – lineetta breve, — lineetta lunga): chi la digita
// a mano in Collocazione a Cascata potrebbe usarne uno diverso da quello
// di riferimento, e un confronto esatto li tratterebbe come cose diverse.
function eRegioneFittizia(nome) {
return /^[-\u2010\u2011\u2012\u2013\u2014\u2015]$/.test(String(nome || '').trim());
}
function caricaCss(url) {
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
document.head.appendChild(link);
}
function caricaJs(url) {
return new Promise(function(resolve, reject) {
var s = document.createElement('script');
s.src = url;
s.onload = resolve;
s.onerror = function() { reject(new Error('impossibile caricare ' + url)); };
document.head.appendChild(s);
});
}
// Stessa normalizzazione (tollerante a disambiguatori tra parentesi,
// accenti, maiuscole, varianti di apostrofo e spazi doppi) già usata in
// Tools.html, per confrontare i nomi della gerarchia geo.json con quelli
// salvati in GeoConfiniIndex.json — e, allo stesso modo, i nomi dei
// luoghi scritti nei Percorsi con quelli reali sulla mappa: un apostrofo
// tipografico (') diventato dritto (') o viceversa copiando un nome da
// un campo all'altro, o uno spazio doppio per errore, non devono far
// sparire silenziosamente una corrispondenza altrimenti valida.
function normalizza(s) {
return String(s || '')
.replace(/\([^)]*\)/g, ' ')
.replace(/[\u2018\u2019\u201B\u02BC\u00B4\u0060]/g, "'")
.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
.toLowerCase()
.replace(/\s+/g, ' ')
.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, '')
.trim();
}
function rimuoviParentesi(nome) {
return String(nome || '').replace(/\s*\([^)]*\)\s*$/, '').trim();
}
$( function() {
var cont = document.getElementById('crep-mappa');
if (!cont) return; // la pagina "Mappa" non ha il contenitore: niente da fare
cont.innerHTML = '<p style="color:#888;">Carico la mappa…</p>';
caricaCss('https://unpkg.com/leaflet@1.9.4/dist/leaflet.css');
caricaCss('https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css');
caricaCss('https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css');
// Stile del popup a più colonne (vedi costruisciPopupLinks): non serve
// un file esterno, basta un <style> iniettato una sola volta. Le
// mitologie si dispongono in colonne da max 15 voci (suddividiInColonne);
// se non stanno tutte affiancate vanno a capo (flex-wrap) invece di
// sfondare lo schermo, come succedeva con la lista verticale unica.
// NB: le colonne usano display:inline-block (non flexbox) di proposito:
// Leaflet calcola la larghezza "naturale" del contenuto del popup con
// un trucco interno (misura offsetWidth/scrollWidth con manipolazioni
// temporanee di stile) che con flexbox restituisce risultati inconsistenti
// e le colonne restano impilate invece di stare affiancate. Con
// inline-block il browser gestisce l'andare a capo in modo nativo ed
// è compatibile con qualunque logica di sizing di Leaflet.
var stilePopup = document.createElement('style');
stilePopup.textContent =
'.crep-mappa-popup { max-width: 640px; }' +
'.crep-mappa-popup-titolo { margin-bottom: 4px; }' +
'.crep-mappa-popup-colonne { font-size: 0; }' + // azzera lo spazio bianco tra inline-block adiacenti
'.crep-mappa-popup-colonna { display: inline-block; vertical-align: top; font-size: 14px; line-height: 1.5; white-space: nowrap; margin: 0 20px 4px 0; }' +
'.crep-mappa-popup-colonna-racconti { border-left: 1px solid rgba(0,0,0,0.15); padding-left: 12px; margin-right: 0; }' +
'.crep-mappa-popup-colonna-drilldown { display: inline-block; vertical-align: top; white-space: normal; width: 220px; font-style: italic; color: #444; margin: 0 20px 4px 0; }';
document.head.appendChild(stilePopup);
caricaJs('https://unpkg.com/leaflet@1.9.4/dist/leaflet.js')
.then(function() { return caricaJs('https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js'); })
.then(inizializzaMappa)
.catch(function(e) {
cont.innerHTML = '<p style="color:#c00;">Errore caricando la libreria della mappa: ' + e.message + '</p>';
});
function inizializzaMappa() {
cont.innerHTML = '';
cont.style.height = cont.style.height || '650px';
// maxBounds + minZoom impediscono di allontanarsi/zoomare oltre
// un singolo mondo, e noWrap sul tile layer impedisce alle tile
// stesse di ripetersi oltre i meridiani ±180° — senza questi tre
// insieme, Leaflet per default permette panning orizzontale
// infinito e i continenti si vedono ripetuti affiancati appena
// lo zoom è basso rispetto alla larghezza del contenitore.
var CONFINI_MONDO = [[-90, -180], [90, 180]];
var mappa = L.map(cont, {
minZoom: 2,
zoomSnap: 0.5,
zoomDelta: 0.5,
maxBounds: CONFINI_MONDO,
maxBoundsViscosity: 1.0
}).setView([20, 10], 2);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors | ' +
'Confini: Natural Earth, ISTAT (geojson-italy), geoBoundaries (CC-BY 4.0) | ' +
'Città: Natural Earth, GeoNames (CC-BY 4.0)',
maxZoom: 18,
noWrap: true
}).addTo(mappa);
var livelloConfini = L.layerGroup().addTo(mappa);
var livelloLuoghi = L.markerClusterGroup();
L.control.layers(null, {
'Aree': livelloConfini,
'Luoghi Puntuali': livelloLuoghi
}, { collapsed: false }).addTo(mappa);
// Pannello filtri per lo strato "Luoghi Puntuali": tre gruppi di
// checkbox (Tipo1 = categoria/colore, Tipo2 = natura del luogo/
// icona, Tipo3 = personaggi coinvolti), incrociabili in AND — un
// marker è visibile solo se soddisfa tutti e tre. Le opzioni sono
// generate dai valori realmente presenti nei dati, tutte
// selezionate di default. Il pannello compare solo quando lo
// strato è attivo (overlayadd/overlayremove, eventi standard del
// control dei layer) e parte sempre richiuso — solo
// l'intestazione "🔍 Filtri", poco ingombrante — espandibile al
// click (vedi costruisciPannelloFiltri).
var PannelloFiltriLuoghi = L.Control.extend({
onAdd: function() {
var cont = L.DomUtil.create('div', 'crep-mappa-filtri-luoghi');
cont.style.cssText = 'background:#fff;padding:8px 10px;border-radius:4px;box-shadow:0 1px 5px rgba(0,0,0,0.4);max-width:110px;font-size:13px;display:none;max-height:70vh;overflow-y:auto;';
L.DomEvent.disableClickPropagation(cont);
this._cont = cont;
return cont;
}
});
var pannelloFiltriLuoghi = new PannelloFiltriLuoghi({ position: 'topright' });
pannelloFiltriLuoghi.addTo(mappa);
mappa.on('overlayadd', function(e) { if (e.name === 'Luoghi Puntuali') pannelloFiltriLuoghi._cont.style.display = ''; });
mappa.on('overlayremove', function(e) { if (e.name === 'Luoghi Puntuali') pannelloFiltriLuoghi._cont.style.display = 'none'; });
// Pannello "Percorsi": mostra sulla mappa SOLO i luoghi curati in
// un percorso (MediaWiki:Percorsi.json, gestito dal pannello
// omonimo di Tools.html), nell'ordine lì stabilito, con una linea
// che li collega in sequenza. Mentre un percorso è attivo, i
// filtri Tipo1/2/3 vengono ignorati (il percorso è una selezione
// esplicita e curata, non un incrocio di categorie) — tornano
// ad applicarsi non appena si sceglie di nuovo "(nessuno)". Stessa
// struttura richiudibile del pannello Filtri, posizionata subito
// sotto di esso.
var PannelloPercorsi = L.Control.extend({
onAdd: function() {
var cont = L.DomUtil.create('div', 'crep-mappa-percorsi');
cont.style.cssText = 'background:#fff;padding:8px 10px;border-radius:4px;box-shadow:0 1px 5px rgba(0,0,0,0.4);max-width:110px;font-size:13px;display:none;max-height:70vh;overflow-y:auto;margin-top:8px;';
L.DomEvent.disableClickPropagation(cont);
this._cont = cont;
return cont;
}
});
var pannelloPercorsi = new PannelloPercorsi({ position: 'topright' });
pannelloPercorsi.addTo(mappa);
mappa.on('overlayadd', function(e) { if (e.name === 'Luoghi Puntuali') pannelloPercorsi._cont.style.display = ''; });
mappa.on('overlayremove', function(e) { if (e.name === 'Luoghi Puntuali') pannelloPercorsi._cont.style.display = 'none'; });
var percorsiEspanso = false;
var percorsiDati = {}; // { "Nome percorso": { luoghi: [ "NomeLuogo1", "NomeLuogo2", ... ] } }
var percorsoAttivo = null; // nome del percorso selezionato, null = nessuno (torna al filtro normale)
var lineaPercorso = null; // L.polyline corrente, se un percorso è attivo
var elAvvisoPercorso = null; // <div> per segnalare visibilmente (non solo in console) tappe non trovate
function mostraAvvisoPercorso(testo) {
if (!elAvvisoPercorso) return;
elAvvisoPercorso.textContent = testo;
elAvvisoPercorso.style.display = testo ? '' : 'none';
}
function costruisciPannelloPercorsi() {
var cont = pannelloPercorsi._cont;
cont.innerHTML = '';
var header = document.createElement('div');
header.style.cssText = 'display:flex;align-items:center;justify-content:space-between;gap:10px;cursor:pointer;font-weight:700;user-select:none;';
var titoloHeader = document.createElement('span');
titoloHeader.textContent = '🗺️ Percorsi';
var freccia = document.createElement('span');
freccia.textContent = percorsiEspanso ? '▲' : '▼';
header.appendChild(titoloHeader);
header.appendChild(freccia);
cont.appendChild(header);
var corpo = document.createElement('div');
corpo.style.display = percorsiEspanso ? '' : 'none';
function creaVoceRadio(etichetta, selezionato, onScegli) {
var label = document.createElement('label');
label.style.cssText = 'display:block;cursor:pointer;padding:2px 0;';
var radio = document.createElement('input');
radio.type = 'radio';
radio.name = 'crep-mappa-percorso-attivo';
radio.checked = selezionato;
radio.style.marginRight = '6px';
radio.addEventListener('change', function() { if (radio.checked) onScegli(); });
label.appendChild(radio);
label.appendChild(document.createTextNode(etichetta));
corpo.appendChild(label);
}
creaVoceRadio('(nessuno)', !percorsoAttivo, function() { attivaPercorso(null); });
Object.keys(percorsiDati).sort(function(a, b) { return a.localeCompare(b, 'it'); }).forEach(function(nome) {
creaVoceRadio(nome, percorsoAttivo === nome, function() { attivaPercorso(nome); });
});
elAvvisoPercorso = document.createElement('div');
elAvvisoPercorso.style.cssText = 'margin-top:8px;padding:6px;background:#fff3cd;border-radius:4px;font-size:12px;color:#856404;display:none;';
corpo.appendChild(elAvvisoPercorso);
cont.appendChild(corpo);
cont.style.maxWidth = percorsiEspanso ? '240px' : '110px';
header.addEventListener('click', function() {
percorsiEspanso = !percorsiEspanso;
corpo.style.display = percorsiEspanso ? '' : 'none';
freccia.textContent = percorsiEspanso ? '▲' : '▼';
cont.style.maxWidth = percorsiEspanso ? '240px' : '110px';
});
}
// Attiva (o disattiva, con nome=null) un percorso: filtra lo
// strato Luoghi mostrando SOLO i luoghi elencati in
// percorsiDati[nome].luoghi, nell'ordine lì scritto, numera il
// popup di ciascuno con la sua tappa, e disegna una linea
// tratteggiata che li collega in sequenza. Un nome di luogo nel
// percorso che non corrisponde a nessun marker attualmente
// caricato (senza coordinate, o pagina non ancora risincronizzata
// con Cargo) viene saltato senza bloccare gli altri — solo
// segnalato in console per chi sta controllando.
function attivaPercorso(nome) {
percorsoAttivo = nome;
if (lineaPercorso) { mappa.removeLayer(lineaPercorso); lineaPercorso = null; }
tuttiIMarkerLuoghi.forEach(function(m) { m.marker.setPopupContent(m.popupBase); });
if (!nome) { applicaFiltroLuoghi(); mostraAvvisoPercorso(''); return; }
var elenco = (percorsiDati[nome] && percorsiDati[nome].luoghi) || [];
livelloLuoghi.clearLayers();
var puntiLinea = [];
var nonTrovati = [];
elenco.forEach(function(nomeLuogo, i) {
// Confronto tollerante (accenti/maiuscole/spazi/apostrofi
// tipografici), non ===: un nome scritto leggermente
// diverso tra il pannello Percorsi e l'anagrafica del
// luogo (es. apostrofo dritto ' invece di ' tipografico)
// non deve far sparire silenziosamente quella tappa.
var m = tuttiIMarkerLuoghi.find(function(x) { return normalizza(x.nome) === normalizza(nomeLuogo); });
if (!m) { nonTrovati.push(nomeLuogo); return; }
m.marker.setPopupContent('<div style="color:#c62828;font-weight:700;">Tappa ' + (i + 1) + '</div>' + m.popupBase);
livelloLuoghi.addLayer(m.marker);
puntiLinea.push(m.marker.getLatLng());
});
if (puntiLinea.length > 1) {
lineaPercorso = L.polyline(puntiLinea, { color: '#c62828', weight: 3, dashArray: '6,6', opacity: 0.85 }).addTo(mappa);
mappa.fitBounds(lineaPercorso.getBounds(), { maxZoom: 8, padding: [40, 40] });
} else if (puntiLinea.length === 1) {
mappa.setView(puntiLinea[0], 6);
}
mostraAvvisoPercorso(nonTrovati.length
? '⚠️ ' + nonTrovati.length + ' luogo/i non trovato/i su questo percorso (nome scritto diversamente rispetto all\'anagrafica, o senza coordinate): ' + nonTrovati.join(', ')
: '');
}
// Breadcrumb di navigazione: un pulsante per ogni livello superiore
// a quello in cui ci si trova, nominato in base a cosa mostra la
// destinazione (non da dove si viene) — es. da Città-view, "Torna a
// Provincia" riporta ai confini delle Province, "Torna a Regione" ai
// confini delle Regioni. Aggiornato da ciascuna funzione di
// drill-down con l'elenco di pulsanti giusto per quel livello.
var BtnBreadcrumb = L.Control.extend({
onAdd: function() {
var cont = L.DomUtil.create('div', 'crep-mappa-breadcrumb');
cont.style.cssText = 'display:flex;flex-direction:column;gap:4px;';
this._cont = cont;
return cont;
}
});
var btnBreadcrumb = new BtnBreadcrumb({ position: 'topleft' });
btnBreadcrumb.addTo(mappa);
function mostraBreadcrumb(pulsanti) {
var cont = btnBreadcrumb._cont;
cont.innerHTML = '';
pulsanti.forEach(function(p) {
var btn = L.DomUtil.create('button', '', cont);
btn.textContent = '⤺ ' + p.testo;
btn.style.cssText = 'padding:6px 10px;background:#fff;border:2px solid rgba(0,0,0,0.2);border-radius:4px;cursor:pointer;text-align:left;';
L.DomEvent.on(btn, 'click', function(e) {
L.DomEvent.stopPropagation(e);
p.onClick();
});
});
}
var cacheFileEsterni = {}; // url -> Promise<geojson>, per non riscaricare due volte lo stesso file
var geoJsonDati = null; // gerarchia da MediaWiki:geo.json
var indiceConfini = {}; // "livello|nome" (normalizzato) -> riga di GeoConfiniIndex.json
function caricaPaginaWikiJson(titolo) {
return $.getJSON(API, {
action: 'query', prop: 'revisions', titles: titolo,
rvprop: 'content', rvslots: 'main', format: 'json'
}).then(function(d) {
var p = d.query.pages[Object.keys(d.query.pages)[0]];
var testo = p.revisions ? p.revisions[0].slots.main['*'] : '{}';
try { return JSON.parse(testo || '{}'); } catch(e) { return {}; }
});
}
function caricaFileEsterno(url) {
if (!cacheFileEsterni[url]) {
cacheFileEsterni[url] = fetch(url).then(function(r) {
if (!r.ok) throw new Error('HTTP ' + r.status + ' su ' + url);
return r.json();
});
}
return cacheFileEsterni[url];
}
// Cerca in indiceConfini la riga per un dato nome, provando in ordine
// più "livelli" candidati (es. ['regione','provincia']): il livello
// taggato da Cargo (quale campo wiki è stato usato: Regione o
// Provincia) non coincide sempre con la profondità scelta a mano in
// Collocazione a Cascata — tipicamente per i Paesi con la convenzione
// della regione fittizia "—", dove un valore "Regione" sul wiki può
// finire annidato un livello più in profondità nell'albero. Tollera
// anche eventuali disambiguatori tra parentesi su uno dei due lati.
function trovaRigaConfine(livelli, nomeGrezzo) {
if (typeof livelli === 'string') livelli = [livelli];
for (var i = 0; i < livelli.length; i++) {
var chiaveEsatta = livelli[i] + '|' + normalizza(nomeGrezzo);
if (indiceConfini[chiaveEsatta]) return indiceConfini[chiaveEsatta];
var chiaveSenzaParentesi = livelli[i] + '|' + normalizza(rimuoviParentesi(nomeGrezzo));
if (indiceConfini[chiaveSenzaParentesi]) return indiceConfini[chiaveSenzaParentesi];
}
return null;
}
function svuota(layer) { layer.clearLayers(); }
function mostraErroreConfini(msg) {
L.popup().setLatLng(mappa.getCenter()).setContent(msg).openOn(mappa);
}
var BASE_WIKI = 'https://www.ilcrepuscolodeglidei.org/index.php';
function urlPagina(titolo) {
return BASE_WIKI + '?title=' + encodeURIComponent(String(titolo).replace(/ /g, '_'));
}
// Colonne mostrate in tabella per ciascuna tabella Cargo, oltre al
// nome pagina — stesso spirito delle colonne scelte nel DPL già in
// uso sul wiki (Nome, Regione, Origine, Tipologia...).
var COLONNE_TABELLA = {
Racconto: ['Regione', 'Origine', 'TipologiaRacconto=Tipologia'],
Voce: ['Regione', 'Origine', 'Tipologia', 'Sottotipologia']
};
// Link a una query Cargo pre-compilata (Special:CargoQuery) che
// elenca in una tabella ordinabile ("dynamic table", il formato
// interattivo di Cargo — equivalente al "table=class=sortable" del
// DPL) le pagine di una tabella con un certo valore nel campo
// indicato. operatore è "=" per i campi singoli (String) o "HOLDS"
// per i campi lista (List (,) of String) — Racconto ha sempre campi
// singoli, mentre in Voce Paese/Regione/Provincia sono liste (Città
// è singola).
function urlCargoQuery(tabella, campo, operatore, valore) {
var where = campo + ' ' + operatore + ' "' + String(valore).replace(/"/g, '\\"') + '"';
var campi = ['_pageName=Nome'].concat(
(COLONNE_TABELLA[tabella] || []).filter(function(c) { return c.split('=')[0] !== campo; })
);
var params = new URLSearchParams({
title: 'Special:CargoQuery',
tables: tabella,
fields: campi.join(','),
where: where,
order_by: '_pageName',
format: 'dynamic table',
limit: '200'
});
return BASE_WIKI + '?' + params.toString();
}
// Campo Cargo corrispondente a ciascun livello (stesso nome sia in
// Voce che in Racconto), e se in Voce quel campo è una lista (serve
// HOLDS) o un valore singolo (serve =). Racconto è sempre singolo.
var CAMPO_CARGO = { paese: 'Paese', regione: 'Regione', provincia: 'Provincia', citta: 'Citta' };
var VOCE_E_LISTA = { paese: true, regione: true, provincia: true, citta: false };
// Indice Paese/Regione/Provincia/Città (normalizzato) -> Set di nomi
// di mitologia (campo Sezione di Voce, che è già di fatto il nome
// della pagina mitologia, es. "Mitologia norrena"). Costruito una
// sola volta all'avvio interrogando Cargo, non richiede nessun JSON
// a parte: il campo Sezione è List (,) of String, quindi una voce
// può già avere più mitologie collegate, e Paese/Regione/Provincia
// idem (Città è invece un valore singolo, ma la stessa funzione di
// indicizzazione funziona comunque anche per un valore solo).
// I popup li usano a QUALSIASI livello di drill-down, non solo
// Paese/Regione.
var mitologiePerPaese = {}, mitologiePerRegione = {}, mitologiePerProvincia = {}, mitologiePerCitta = {};
// Quale indice usare per un dato livello di drill-down — le chiavi
// di questo oggetto DEVONO combaciare con i valori di "livello"
// passati a costruisciPopupLinks in giro per il file.
var INDICE_MITOLOGIE_PER_LIVELLO = {
paese: mitologiePerPaese,
regione: mitologiePerRegione,
provincia: mitologiePerProvincia,
citta: mitologiePerCitta
};
function aggiungiAIndiceMitologie(indice, valoriCampoGrezzo, valoriSezioneGrezzo) {
var luoghi = String(valoriCampoGrezzo || '').split(',').map(function(s) { return s.trim(); }).filter(Boolean);
var sezioni = String(valoriSezioneGrezzo || '').split(',').map(function(s) { return s.trim(); }).filter(Boolean);
if (!luoghi.length || !sezioni.length) return;
luoghi.forEach(function(l) {
var chiave = normalizza(l);
if (!indice[chiave]) indice[chiave] = new Set();
sezioni.forEach(function(s) { indice[chiave].add(s); });
});
}
// Interroga Cargo (paginando: la tabella Voce può avere migliaia di
// righe) per costruire gli indici Paese/Regione -> Mitologie una
// sola volta, in parallelo al resto del caricamento iniziale.
function caricaMitologie() {
var offset = 0, limite = 500;
function pagina() {
return $.getJSON(API, {
action: 'cargoquery', tables: 'Voce', fields: 'Paese,Regione,Provincia,Citta,Sezione',
limit: limite, offset: offset, format: 'json'
}).then(function(risposta) {
if (risposta.error) throw new Error(risposta.error.info || JSON.stringify(risposta.error));
var righe = (risposta.cargoquery || []).map(function(r) { return r.title; });
righe.forEach(function(r) {
aggiungiAIndiceMitologie(mitologiePerPaese, r.Paese, r.Sezione);
aggiungiAIndiceMitologie(mitologiePerRegione, r.Regione, r.Sezione);
aggiungiAIndiceMitologie(mitologiePerProvincia, r.Provincia, r.Sezione);
aggiungiAIndiceMitologie(mitologiePerCitta, r.Citta, r.Sezione);
});
if (righe.length < limite) return;
offset += limite;
return pagina();
});
}
return pagina().catch(function(e) {
console.error('Errore caricando le mitologie da Cargo (i popup funzioneranno comunque, solo senza questi link):', e);
});
}
// Testo grezzo di una pagina wiki (non JSON) — serve per estrarre gli
// oggetti JS areePerContinente/paesiPerArea da MediaWiki:Common.js,
// stessa tecnica già usata dal pannello Mitologie di Tools.html.
async function caricaTestoWiki(titolo) {
var risposta = await $.getJSON(API, { action: 'query', prop: 'revisions', titles: titolo, rvprop: 'content', rvslots: 'main', format: 'json' });
var pagina = risposta.query.pages[Object.keys(risposta.query.pages)[0]];
return pagina.revisions ? pagina.revisions[0].slots.main['*'] : '';
}
function estraiOggettoJs(testo, nomeVar) {
var re = new RegExp('var ' + nomeVar + '\\s*=\\s*(\\{[\\s\\S]*?\\});\\s*\\n');
var m = re.exec(testo || '');
if (!m) return {};
var oggetto;
try { oggetto = (new Function('return ' + m[1]))(); } catch(e) { return {}; }
var out = {};
Object.keys(oggetto || {}).forEach(function(k) {
var arr = Array.isArray(oggetto[k]) ? oggetto[k] : [];
out[k] = arr.filter(function(v) { return v !== ''; });
});
return out;
}
// Carica MediaWiki:Mitologie.json (l'abbinamento curato a mano dal
// pannello Mitologie di Tools.html: percorsi Continente>Area>Paese>
// Regione>Provincia, anche parziali) e lo unisce (non sostituisce)
// agli indici già costruiti da Cargo sopra — se un Paese/Regione ha
// una mitologia sia da Cargo sia dal file curato, compaiono entrambe,
// senza duplicati. Un percorso parziale (es. solo Area, senza Paese)
// si espande a tutti i Paesi di quell'Area/Continente; un percorso
// senza Regione esplicita si espande a tutte le Regioni note di quel
// Paese (via geo.json).
function caricaMitologieCurate() {
return Promise.all([
caricaTestoWiki('MediaWiki:Common.js'),
caricaPaginaWikiJson('MediaWiki:Mitologie.json')
]).then(function(risultati) {
var areePerContinente = estraiOggettoJs(risultati[0], 'areePerContinente');
var paesiPerArea = estraiOggettoJs(risultati[0], 'paesiPerArea');
var mitologieDati = risultati[1] || {};
function espandiPaesiDelPercorso(p) {
if (p.paese) return [p.paese];
if (p.area) return (paesiPerArea[p.area] || []);
if (p.continente) {
var paesi = [];
(areePerContinente[p.continente] || []).forEach(function(a) { paesi = paesi.concat(paesiPerArea[a] || []); });
return paesi;
}
return [];
}
function espandiRegioniDelPercorso(p) {
if (p.regione) return [p.regione];
var regioni = [];
espandiPaesiDelPercorso(p).forEach(function(paese) {
regioni = regioni.concat(Object.keys(geoJsonDati[paese] || {}).filter(function(r) { return !eRegioneFittizia(r); }));
});
return regioni;
}
// Espande le Province indicate dal percorso: se p.provincia
// è esplicito, usa solo quello; altrimenti prende tutte le
// Province di tutte le Regioni dei Paesi del percorso — qui,
// a differenza di espandiRegioniDelPercorso, la regione
// fittizia "—" NON va filtrata: è così che i Paesi senza
// vero livello Regione (es. Canada) tengono le Province in
// geo.json, e va comunque attraversata per raggiungerle.
function espandiProvinceDelPercorso(p) {
if (p.provincia) return [p.provincia];
var province = [];
espandiPaesiDelPercorso(p).forEach(function(paese) {
var regioniDiQuestoPaese = p.regione ? [p.regione] : Object.keys(geoJsonDati[paese] || {});
regioniDiQuestoPaese.forEach(function(regione) {
province = province.concat(Object.keys((geoJsonDati[paese] || {})[regione] || {}));
});
});
return province;
}
// Espande le Città indicate dal percorso: se p.citta è
// esplicito, usa solo quello; altrimenti tutte le Città di
// tutte le Province (esplicite o espanse) del percorso.
function espandiCittaDelPercorso(p) {
if (p.citta) return [p.citta];
var citta = [];
espandiPaesiDelPercorso(p).forEach(function(paese) {
var regioniDiQuestoPaese = p.regione ? [p.regione] : Object.keys(geoJsonDati[paese] || {});
regioniDiQuestoPaese.forEach(function(regione) {
var provinceDiQuestaRegione = p.provincia ? [p.provincia] : Object.keys((geoJsonDati[paese] || {})[regione] || {});
provinceDiQuestaRegione.forEach(function(provincia) {
citta = citta.concat(((geoJsonDati[paese] || {})[regione] || {})[provincia] || []);
});
});
});
return citta;
}
Object.keys(mitologieDati).forEach(function(nomeMitologia) {
(mitologieDati[nomeMitologia].percorsi || []).forEach(function(percorso) {
espandiPaesiDelPercorso(percorso).forEach(function(paese) {
var chiave = normalizza(paese);
if (!mitologiePerPaese[chiave]) mitologiePerPaese[chiave] = new Set();
mitologiePerPaese[chiave].add(nomeMitologia);
});
espandiRegioniDelPercorso(percorso).forEach(function(regione) {
var chiave = normalizza(regione);
if (!mitologiePerRegione[chiave]) mitologiePerRegione[chiave] = new Set();
mitologiePerRegione[chiave].add(nomeMitologia);
});
espandiProvinceDelPercorso(percorso).forEach(function(provincia) {
var chiave = normalizza(provincia);
if (!mitologiePerProvincia[chiave]) mitologiePerProvincia[chiave] = new Set();
mitologiePerProvincia[chiave].add(nomeMitologia);
});
espandiCittaDelPercorso(percorso).forEach(function(citta) {
var chiave = normalizza(citta);
if (!mitologiePerCitta[chiave]) mitologiePerCitta[chiave] = new Set();
mitologiePerCitta[chiave].add(nomeMitologia);
});
});
});
}).catch(function(e) {
console.error('Errore caricando le mitologie curate da Mitologie.json (restano comunque quelle derivate da Cargo, se disponibili):', e);
});
}
// Verifica (con cache, una sola chiamata per titolo) se una pagina
// esiste davvero sul wiki — usata per non mostrare mai un link
// "Racconti" rotto quando la pagina Racconti:Nome non esiste.
var esistenzaPaginaCache = {};
function paginaEsiste(titolo) {
if (!esistenzaPaginaCache[titolo]) {
esistenzaPaginaCache[titolo] = $.getJSON(API, { action: 'query', titles: titolo, format: 'json' })
.then(function(r) {
var pagine = r.query.pages;
var id = Object.keys(pagine)[0];
return !('missing' in pagine[id]);
})
.catch(function() { return false; });
}
return esistenzaPaginaCache[titolo];
}
var SOGLIA_MITOLOGIE_DIRETTE = 15;
// Testo dell'aggettivo di livello usato nel messaggio "scendi al
// livello X per vederle" quando le mitologie sono troppe (vedi
// costruisciPopupLinks) — solo per i livelli che hanno un livello
// sottostante su cui scendere (Città è terminale, non compare qui).
var SUCCESSIVO_TESTO = { paese: 'regionale', regione: 'provinciale', provincia: 'cittadino' };
// Divide un elenco di link (stringhe HTML già pronte) in colonne da al
// massimo SOGLIA_MITOLOGIE_DIRETTE elementi ciascuna. Usata solo come
// fallback quando le mitologie sono tante ma non c'è un livello
// sottostante su cui proporre di scendere (tipicamente a livello
// Città, che è terminale) — altrimenti si preferisce il messaggio di
// drill-down (vedi costruisciPopupLinks).
function suddividiInColonne(elementi) {
var colonne = [];
for (var i = 0; i < elementi.length; i += SOGLIA_MITOLOGIE_DIRETTE) {
colonne.push(elementi.slice(i, i + SOGLIA_MITOLOGIE_DIRETTE));
}
return colonne;
}
// Contenuto del popup mostrato al passaggio del mouse: link alle
// pagine di mitologia collegate a QUALSIASI livello (Paese, Regione,
// Provincia, Città — da Mitologie.json, può essere più d'una o
// nessuna) e Racconti (solo se la pagina "Racconti:Nome" esiste
// davvero). "Creature fantastiche" è sospeso per ora, da rivedere
// più avanti. Ritorna una Promise perché il controllo di esistenza
// della pagina Racconti richiede una chiamata API.
//
// Quando le mitologie sono più di SOGLIA_MITOLOGIE_DIRETTE e c'è un
// livello sottostante su cui scendere (testoLivelloSuccessivo
// valorizzato), si mostra un messaggio con un link per scendere di
// livello invece di elencarle tutte (es. gli USA hanno decine di
// mitologie native, molto più leggibili una volta scese a livello
// Regione). Se non c'è un livello sottostante (Città) o comunque non
// è stato passato un testoLivelloSuccessivo, si ripiega sull'elenco
// multi-colonna (max 15 voci a colonna). La colonna di "Racconti" è
// sempre una colonna a parte, mai in coda alla lista di mitologie.
function costruisciPopupLinks(nome, livello, testoLivelloSuccessivo) {
var titoloRacconti = 'Racconti:' + nome;
return paginaEsiste(titoloRacconti).then(function(esisteRacconti) {
var indice = INDICE_MITOLOGIE_PER_LIVELLO[livello] || {};
var mitologie = Array.from(indice[normalizza(nome)] || []);
var html = '<div class="crep-mappa-popup">' +
'<div class="crep-mappa-popup-titolo"><strong>' + nome + '</strong></div>';
if (mitologie.length || esisteRacconti) {
html += '<div class="crep-mappa-popup-colonne">';
if (mitologie.length > SOGLIA_MITOLOGIE_DIRETTE && testoLivelloSuccessivo) {
html += '<div class="crep-mappa-popup-colonna crep-mappa-popup-colonna-drilldown">' +
mitologie.length + ' mitologie collegate — <a href="#" class="crep-mappa-popup-drilldown">scendi al livello ' +
testoLivelloSuccessivo + '</a> per vederle</div>';
} else {
var linkMitologie = mitologie.map(function(m) {
return '<a href="' + urlPagina(m) + '" target="_blank">📖 ' + m + '</a>';
});
suddividiInColonne(linkMitologie).forEach(function(colonna) {
html += '<div class="crep-mappa-popup-colonna">' + colonna.join('<br>') + '</div>';
});
}
if (esisteRacconti) {
html += '<div class="crep-mappa-popup-colonna crep-mappa-popup-colonna-racconti">' +
'<a href="' + urlPagina(titoloRacconti) + '" target="_blank">📜 Racconti</a></div>';
}
html += '</div>';
}
html += '</div>';
return html;
});
}
// Apre il popup al passaggio del mouse sul layer, e lo tiene aperto
// anche quando il mouse si sposta DENTRO al popup stesso (altrimenti,
// uscendo dall'area del layer per raggiungere un link nel popup, il
// mouseout lo chiuderebbe prima di poterci cliccare). Chiude con un
// piccolo ritardo, annullato se il mouse rientra in tempo.
function agganciaPopupHover(layer) {
var timeoutChiusura = null;
function annullaChiusura() {
if (timeoutChiusura) { clearTimeout(timeoutChiusura); timeoutChiusura = null; }
}
function chiudiConRitardo() {
annullaChiusura();
timeoutChiusura = setTimeout(function() { layer.closePopup(); }, 200);
}
layer.on('mouseover', function() { annullaChiusura(); layer.openPopup(); });
layer.on('mouseout', chiudiConRitardo);
layer.on('popupopen', function(e) {
var el = e.popup.getElement ? e.popup.getElement() : e.popup._container;
if (!el) return;
el.addEventListener('mouseenter', annullaChiusura);
el.addEventListener('mouseleave', chiudiConRitardo);
});
}
// --- Disegna un insieme di poligoni (Paesi, Regioni o Province) a
// partire da una lista di {nome, riga} e da un gestore di click
// (che riceve il nome cliccato e fa lo zoom/drill-down successivo).
// "livello" determina i link mostrati al passaggio del mouse. ---
function disegnaPoligoni(elenco, onClick, livello) {
svuota(livelloConfini);
var perFile = {};
elenco.forEach(function(e) {
if (!e.riga || !e.riga.file || !e.riga.feature_id) return;
(perFile[e.riga.file] = perFile[e.riga.file] || []).push(e);
});
if (!Object.keys(perFile).length) {
mostraErroreConfini('Nessun confine con poligono assegnato qui — vanno abbinati prima in Tools.html (pannello Confini Geografici).');
return;
}
var bounds = [];
Object.keys(perFile).forEach(function(url) {
caricaFileEsterno(url).then(function(geo) {
var mappaFeatureId = {};
perFile[url].forEach(function(e) { mappaFeatureId[String(e.riga.feature_id)] = e; });
(geo.features || []).forEach(function(f) {
var p = f.properties || {};
var candidati = [p.ISO_A3, p.ADM0_A3, p.reg_iso_3166_2, p.prov_acr, p.shapeISO, p.shapeID, p.NE_ID].filter(function(x) { return x != null; }).map(String);
var match = null;
for (var i = 0; i < candidati.length; i++) { if (mappaFeatureId[candidati[i]]) { match = mappaFeatureId[candidati[i]]; break; } }
if (!match) return;
var layer = L.geoJSON(f, { style: { color: '#1a4a8e', weight: 1.5, fillOpacity: 0.08, fillColor: '#1a4a8e' } });
layer.bindPopup('<strong>' + match.nome + '</strong>', { closeButton: false, maxWidth: 640 });
layer.on('popupopen', function() {
costruisciPopupLinks(match.nome, livello, SUCCESSIVO_TESTO[livello]).then(function(html) {
layer.setPopupContent(html);
// Se costruisciPopupLinks ha generato il messaggio "troppe
// mitologie" (vedi SOGLIA_MITOLOGIE_DIRETTE), il link dentro
// deve scendere di livello esattamente come un click sul
// poligono: stesso "onClick" già usato sotto, non una nuova
// funzione — così restano sempre sincronizzati.
var popup = layer.getPopup();
var el = popup && popup.getElement ? popup.getElement() : null;
var linkDrill = el && el.querySelector('.crep-mappa-popup-drilldown');
if (linkDrill) {
linkDrill.addEventListener('click', function(ev) {
ev.preventDefault();
layer.closePopup();
onClick(match.nome);
});
}
});
});
layer.on('mouseover', function() { layer.setStyle({ fillOpacity: 0.25 }); });
layer.on('mouseout', function() { layer.setStyle({ fillOpacity: 0.08 }); });
agganciaPopupHover(layer);
layer.on('click', function() { onClick(match.nome); });
layer.addTo(livelloConfini);
try { bounds.push(layer.getBounds()); } catch(e2) {}
});
if (bounds.length) {
var tot = bounds[0];
for (var i = 1; i < bounds.length; i++) tot = tot.extend(bounds[i]);
mappa.fitBounds(tot, { maxZoom: 6 });
}
}).catch(function(e) {
mostraErroreConfini('Errore scaricando i confini (' + url + '): ' + e.message);
});
});
}
// --- Disegna le Città (marker singoli, non poligoni: lat/lon già
// pronte). Mostra sia le mitologie collegate a livello Città (via
// costruisciPopupLinks, livello 'citta') sia Racconti se la pagina
// esiste; Città è un livello terminale, quindi non passa nessun
// testoLivelloSuccessivo — se le mitologie fossero comunque troppe,
// costruisciPopupLinks ripiega sulle colonne invece del drill-down.
// "Creature fantastiche" resta sospeso per ora, da rivedere più avanti. ---
function disegnaCitta(elenco) {
svuota(livelloConfini);
var bounds = [];
elenco.forEach(function(e) {
if (!e.riga || typeof e.riga.lat !== 'number' || typeof e.riga.lon !== 'number') return;
var marker = L.marker([e.riga.lat, e.riga.lon]);
marker.bindPopup('<strong>' + e.nome + '</strong>', { closeButton: false, maxWidth: 640 });
marker.on('popupopen', function() {
costruisciPopupLinks(e.nome, 'citta').then(function(html) { marker.setPopupContent(html); });
});
agganciaPopupHover(marker);
marker.addTo(livelloConfini);
bounds.push([e.riga.lat, e.riga.lon]);
});
if (bounds.length) mappa.fitBounds(bounds, { maxZoom: 10 });
else mostraErroreConfini('Nessuna Città con coordinate assegnate qui.');
}
// --- Livello Paesi (punto di partenza): TUTTI i Paesi con un confine
// reale in GeoConfiniIndex.json, non solo quelli già organizzati in
// geo.json — la gerarchia serve solo a partire dal drill-down nelle
// Regioni, non per sapere quali Paesi esistono. ---
function mostraPaesi() {
mostraBreadcrumb([]);
var elenco = Object.keys(indiceConfini)
.filter(function(k) { return k.indexOf('paese|') === 0; })
.map(function(k) { var r = indiceConfini[k]; return { nome: r.nome, riga: r }; });
disegnaPoligoni(elenco, drillDownRegioni, 'paese');
}
// --- Livello Regioni di un Paese ---
function drillDownRegioni(nomePaese) {
mostraBreadcrumb([{ testo: 'Torna al Mondo', onClick: mostraPaesi }]);
// Il Paese cliccato viene dal confine reale (GeoConfiniIndex), il
// suo nome può quindi non coincidere esattamente con una chiave di
// geo.json (es. differenze di maiuscole/accenti raro ma possibile):
// si cerca la chiave giusta con un confronto tollerante.
var chiavePaese = Object.keys(geoJsonDati).filter(function(p) { return normalizza(p) === normalizza(nomePaese); })[0];
var regioni = chiavePaese ? geoJsonDati[chiavePaese] : null;
if (!regioni) {
mostraErroreConfini('<strong>' + nomePaese + '</strong> non ha ancora Regioni organizzate in geo.json (Collocazione a Cascata) — solo il confine del Paese è disponibile per ora.');
return;
}
var chiavi = Object.keys(regioni);
// Paese senza vero livello Regione (convenzione di un trattino
// da solo): salta direttamente alle Province, come se il click
// fosse già avvenuto sulla regione fittizia. Si passa la chiave
// reale trovata (chiavi[0]), non un valore fisso, perché il
// trattino esatto digitato a mano può variare.
if (chiavi.length === 1 && eRegioneFittizia(chiavi[0])) {
drillDownProvince(chiavePaese, chiavi[0]);
return;
}
var elenco = chiavi.map(function(regione) {
return { nome: regione, riga: trovaRigaConfine(['regione', 'provincia'], regione) };
});
disegnaPoligoni(elenco, function(nomeRegione) { drillDownProvince(chiavePaese, nomeRegione); }, 'regione');
}
// --- Livello Province di una Regione ---
function drillDownProvince(nomePaese, nomeRegione) {
var pulsanti = [{ testo: 'Torna al Mondo', onClick: mostraPaesi }];
// Se la Regione è quella fittizia (Paesi senza vero livello
// Regione, es. Canada), "Torna a Regione" non ha senso: quella
// vista si auto-salterebbe di nuovo alle Province.
if (!eRegioneFittizia(nomeRegione)) {
pulsanti.push({ testo: 'Torna a Regione', onClick: function() { drillDownRegioni(nomePaese); } });
}
mostraBreadcrumb(pulsanti);
var province = (geoJsonDati[nomePaese] || {})[nomeRegione] || {};
var elenco = Object.keys(province).map(function(provincia) {
return { nome: provincia, riga: trovaRigaConfine(['provincia', 'regione'], provincia) };
});
disegnaPoligoni(elenco, function(nomeProvincia) { drillDownCitta(nomePaese, nomeRegione, nomeProvincia); }, 'provincia');
}
// --- Livello Città di una Provincia (marker, non poligoni) ---
function drillDownCitta(nomePaese, nomeRegione, nomeProvincia) {
var pulsanti = [{ testo: 'Torna al Mondo', onClick: mostraPaesi }];
if (!eRegioneFittizia(nomeRegione)) {
pulsanti.push({ testo: 'Torna a Regione', onClick: function() { drillDownRegioni(nomePaese); } });
}
pulsanti.push({ testo: 'Torna a Provincia', onClick: function() { drillDownProvince(nomePaese, nomeRegione); } });
mostraBreadcrumb(pulsanti);
var citta = ((geoJsonDati[nomePaese] || {})[nomeRegione] || {})[nomeProvincia] || [];
var elenco = citta.map(function(c) {
return { nome: c, riga: trovaRigaConfine('citta', c) };
});
disegnaCitta(elenco);
}
// --- Strato Luoghi (indipendente, sempre disponibile) ---
// Tipo1 = categoria di contenuto (colore del pallino): a cosa si
// riferisce il luogo — Mito, Mistero, Itinerario Arcano, Racconto.
// Tipo2 = natura fisica del luogo (icona dentro il pallino): Città,
// Palazzo, Ponte, Lago, Fiume... elenco volutamente aperto — per
// aggiungerne uno nuovo basta una riga in ICONA_PER_TIPO2 (con la
// grafia "vera", maiuscole e accenti compresi: è anche quello che
// il pannello Anagrafica Luoghi di Tools.html legge per popolare i
// suggerimenti di Tipo2, leggendo questo stesso file). Un Tipo2
// non ancora in elenco resta comunque visibile con l'icona
// generica di default, non sparisce.
var COLORE_PER_TIPO1 = {
mito: '#1a4a8e',
racconto: '#2e7d32',
mistero: '#6a1b9a',
'itinerario arcano': '#e65100',
_default: '#616161'
};
var ICONA_PER_TIPO2 = {
'Città': '🏙️',
'Palazzo': '🏰',
'Tempio': '🏛️',
'Ponte': '🌉',
'Lago': '🏞️',
'Fiume': '💧',
'Mare': '🌊',
'Montagna': '⛰️',
'Vulcano': '🌋',
'Grotta': '🕳️',
'Foresta': '🌲',
'Isola': '🏝️',
'Valle': '🌄',
'Rovine': '🏚️',
'Tomba': '🪦',
'Porto': '⚓',
'Deserto': '🏜️',
'Campo di battaglia': '⚔️',
'Sorgente': '⛲',
'Piazza': '◻️'
};
var ICONA_PER_TIPO2_DEFAULT = '📍';
// Icone "pure" (senza cerchio) per Tipo3 — affiancate al pallino
// principale, una per ogni valore presente (fino a 3). A
// differenza di Tipo2, un valore non ancora in elenco qui non
// mostra nessuna icona di fallback (niente di "generico" da
// mostrare per un personaggio non ancora classificato): va
// semplicemente aggiunta una riga qui quando se ne definisce uno
// nuovo. Qui invece — a differenza di Tipo2 — il pannello
// Anagrafica Luoghi NON legge questa mappa: i suggerimenti di
// Tipo3 vengono presi dai valori già usati in almeno un luogo
// esistente, non da un elenco predefinito.
var ICONA_PER_TIPO3 = {
'Umani': '🧑',
'Semidei': '🌗',
'Divinità': '⚡',
'Fantasmi': '👻',
'Animali Fantastici': '🐉',
'Piccolo Popolo': '🧚',
'Eroi': '🛡️'
};
// Indice normalizzato (accento/maiuscole-insensibile) costruito
// una sola volta dalle mappe "vere" sopra — evita di dover
// mantenere due mappe parallele (una per la grafia corretta, una
// per la lookup) che rischierebbero di disallinearsi.
function costruisciIndiceNormalizzato(mappa) {
var indice = {};
Object.keys(mappa).forEach(function(k) { indice[normalizza(k)] = mappa[k]; });
return indice;
}
var ICONA_PER_TIPO2_NORM = costruisciIndiceNormalizzato(ICONA_PER_TIPO2);
var ICONA_PER_TIPO3_NORM = costruisciIndiceNormalizzato(ICONA_PER_TIPO3);
// Come valoriDistinti, ma per campi "List (,) of String" come
// Tipo3, dove una riga può avere più valori insieme separati da
// virgola (es. "Eroi, Fantasma"): ogni singolo valore va tenuto
// separato nell'elenco delle opzioni del filtro, non l'intera
// stringa come un blocco unico.
function valoriDistintiMultivalore(righe, campo) {
var trovati = new Set();
var haVuoti = false;
righe.forEach(function(r) {
var parti = String(r[campo] || '').split(',').map(function(s) { return s.trim(); }).filter(Boolean);
if (!parti.length) { haVuoti = true; return; }
parti.forEach(function(p) { trovati.add(p); });
});
var lista = Array.from(trovati).sort(function(a, b) { return a.localeCompare(b, 'it'); });
if (haVuoti) lista.push('');
return lista;
}
// Pallino principale (colore=Tipo1, icona dentro=Tipo2) più, in
// fila accanto, un'icona pura (senza cerchio, leggermente più
// piccola) per ciascun valore di Tipo3 presente (max 3, un
// valore senza icona nota in ICONA_PER_TIPO3 viene saltato senza
// lasciare spazi vuoti). L'ancora del marker resta sul centro
// del pallino principale, non sul blocco intero: aggiungere le
// iconcine a fianco non sposta mai il punto geografico reale.
var DIMENSIONE_PIN = 22;
var DIMENSIONE_ICONA_TIPO3 = 15;
function iconaPerLuogo(tipo1, tipo2, tipo3Lista) {
var colore = COLORE_PER_TIPO1[normalizza(tipo1)] || COLORE_PER_TIPO1._default;
var emoji2 = ICONA_PER_TIPO2_NORM[normalizza(tipo2)] || ICONA_PER_TIPO2_DEFAULT;
var iconeTipo3 = (tipo3Lista || [])
.map(function(t) { return ICONA_PER_TIPO3_NORM[normalizza(t)] || ''; })
.filter(Boolean);
var htmlTipo3 = iconeTipo3.map(function(e) {
return '<span style="font-size:' + DIMENSIONE_ICONA_TIPO3 + 'px;line-height:1;text-shadow:0 0 3px #fff,0 0 3px #fff,0 0 3px #fff,0 0 3px #fff;">' + e + '</span>';
}).join('');
var larghezzaExtra = iconeTipo3.length ? (iconeTipo3.length * (DIMENSIONE_ICONA_TIPO3 + 3) + 3) : 0;
var html = '<div style="display:flex;align-items:center;gap:3px;">' +
'<div style="flex-shrink:0;background:' + colore + ';width:' + DIMENSIONE_PIN + 'px;height:' + DIMENSIONE_PIN + 'px;border-radius:50%;border:2px solid #fff;box-shadow:0 0 2px rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;font-size:12px;line-height:1;">' + emoji2 + '</div>' +
htmlTipo3 +
'</div>';
return L.divIcon({
className: 'crep-mappa-luogo-icona',
html: html,
iconSize: [DIMENSIONE_PIN + larghezzaExtra, DIMENSIONE_PIN],
iconAnchor: [DIMENSIONE_PIN / 2, DIMENSIONE_PIN / 2]
});
}
// --- Filtro dello strato Luoghi per Tipo1/Tipo2/Tipo3 (pannello checkbox) ---
var tuttiIMarkerLuoghi = []; // [{marker, tipo1, tipo2, tipo3}] — stringhe originali (trim), '' se vuoto
// --- SOLO DIAGNOSTICA TEMPORANEA (da rimuovere una volta risolto
// il problema di abbinamento dei nomi nei Percorsi) --- espone
// sulla Console del browser i dati altrimenti "privati" dentro
// questa funzione, per poterli ispezionare da fuori senza dover
// indovinare cosa succede. Le funzioni-getter (non uno snapshot
// diretto) vedono sempre il valore aggiornato di
// tuttiIMarkerLuoghi/percorsiDati anche se vengono riassegnati
// più avanti (sono chiusure sulla stessa variabile, non copie).
window.crepDebugMappa = {
getMarkers: function() { return tuttiIMarkerLuoghi; },
getPercorsi: function() { return percorsiDati; },
normalizza: normalizza
};
var filtroTipo1 = new Set(); // valori di Tipo1 attualmente selezionati (checkbox spuntate)
var filtroTipo2 = new Set(); // valori di Tipo2 attualmente selezionati
var filtroTipo3 = new Set(); // valori di Tipo3 attualmente selezionati (personaggi coinvolti)
var ORDINE_TIPO1 = ['Mito', 'Racconto', 'Mistero', 'Itinerario Arcano']; // ordine di visualizzazione preferito, non vincolante
// Valori distinti (stringa originale, non normalizzata) presenti in
// righe[campo], ordinati secondo ordinePreferito quando dato e poi
// alfabeticamente; un valore vuoto (Tipo1/Tipo2/Tipo3 non compilato)
// va sempre in fondo come '' — il pannello lo mostra come "(non
// specificato)" ma il confronto nel filtro resta sulla stringa vuota.
function valoriDistinti(righe, campo, ordinePreferito) {
var trovati = new Set();
var haVuoti = false;
righe.forEach(function(r) {
var v = String(r[campo] || '').trim();
if (v) trovati.add(v); else haVuoti = true;
});
var lista = Array.from(trovati).sort(function(a, b) {
var ia = ordinePreferito ? ordinePreferito.indexOf(a) : -1;
var ib = ordinePreferito ? ordinePreferito.indexOf(b) : -1;
if (ia !== -1 && ib !== -1) return ia - ib;
if (ia !== -1) return -1;
if (ib !== -1) return 1;
return a.localeCompare(b, 'it');
});
if (haVuoti) lista.push('');
return lista;
}
// Ricostruisce lo strato Luoghi mostrando solo i marker il cui
// Tipo1 È tra quelli selezionati E il cui Tipo2 è tra quelli
// selezionati E il cui Tipo3 ha ALMENO UNO dei suoi valori tra
// quelli selezionati (Tipo3 può avere più valori insieme, es.
// "Eroi, Fantasma": il marker resta visibile se anche solo uno
// dei due è spuntato — OR dentro Tipo3, AND fra i tre gruppi).
function applicaFiltroLuoghi() {
livelloLuoghi.clearLayers();
tuttiIMarkerLuoghi.forEach(function(m) {
var tipo3Ok = m.tipo3.some(function(t) { return filtroTipo3.has(t); });
if (filtroTipo1.has(m.tipo1) && filtroTipo2.has(m.tipo2) && tipo3Ok) livelloLuoghi.addLayer(m.marker);
});
}
// Disegna un gruppo di checkbox (tutte spuntate all'inizio, uno
// stato --> un Set) dentro "cont", con titolo "etichetta".
function disegnaGruppoCheckbox(cont, etichetta, valori, insiemeSelezionati) {
var titolo = document.createElement('div');
titolo.style.cssText = 'font-weight:700;margin:8px 0 4px;';
titolo.textContent = etichetta;
cont.appendChild(titolo);
valori.forEach(function(v) {
var label = document.createElement('label');
label.style.cssText = 'display:block;cursor:pointer;padding:1px 0;';
var cb = document.createElement('input');
cb.type = 'checkbox';
cb.checked = true;
cb.style.marginRight = '6px';
cb.addEventListener('change', function() {
if (cb.checked) insiemeSelezionati.add(v); else insiemeSelezionati.delete(v);
applicaFiltroLuoghi();
});
label.appendChild(cb);
label.appendChild(document.createTextNode(v || '(non specificato)'));
cont.appendChild(label);
});
}
// Il pannello parte sempre richiuso (solo l'intestazione, poco
// ingombrante) e si espande al click — lo stato resta ricordato
// tra un ricalcolo dei filtri e l'altro (es. dopo un "Aggiorna
// Cargo"), non si richiude da solo ogni volta che i dati cambiano.
var pannelloFiltriEspanso = false;
function costruisciPannelloFiltri(distintiTipo1, distintiTipo2, distintiTipo3) {
var cont = pannelloFiltriLuoghi._cont;
cont.innerHTML = '';
var header = document.createElement('div');
header.style.cssText = 'display:flex;align-items:center;justify-content:space-between;gap:10px;cursor:pointer;font-weight:700;user-select:none;';
var titoloHeader = document.createElement('span');
titoloHeader.textContent = '🔍 Filtri';
var freccia = document.createElement('span');
freccia.textContent = pannelloFiltriEspanso ? '▲' : '▼';
header.appendChild(titoloHeader);
header.appendChild(freccia);
cont.appendChild(header);
var corpo = document.createElement('div');
corpo.style.display = pannelloFiltriEspanso ? '' : 'none';
disegnaGruppoCheckbox(corpo, 'Tipo', distintiTipo1, filtroTipo1);
disegnaGruppoCheckbox(corpo, 'Natura del luogo', distintiTipo2, filtroTipo2);
disegnaGruppoCheckbox(corpo, 'Personaggi coinvolti', distintiTipo3, filtroTipo3);
cont.appendChild(corpo);
cont.style.maxWidth = pannelloFiltriEspanso ? '220px' : '110px';
header.addEventListener('click', function() {
pannelloFiltriEspanso = !pannelloFiltriEspanso;
corpo.style.display = pannelloFiltriEspanso ? '' : 'none';
freccia.textContent = pannelloFiltriEspanso ? '▲' : '▼';
cont.style.maxWidth = pannelloFiltriEspanso ? '220px' : '110px';
});
}
function caricaLuoghi() {
var righe = [];
var offset = 0, limite = 500, continua = true;
function pagina() {
return $.getJSON(API, {
action: 'cargoquery', tables: 'Luoghi',
fields: '_pageName=pagina,Nome,Ancora,Lat,Lon,Tipo1,Tipo2,Tipo3,Descrizione',
where: "Lat IS NOT NULL AND Lat<>'' AND Lon IS NOT NULL AND Lon<>''",
limit: limite, offset: offset, format: 'json'
}).then(function(risposta) {
var pag = (risposta.cargoquery || []).map(function(r) { return r.title; });
righe = righe.concat(pag);
if (pag.length < limite) { continua = false; return; }
offset += limite;
return pagina();
});
}
return pagina().then(function() { return righe; });
}
caricaLuoghi().then(function(righe) {
tuttiIMarkerLuoghi = [];
righe.forEach(function(r) {
var lat = parseFloat(r.Lat), lon = parseFloat(r.Lon);
if (isNaN(lat) || isNaN(lon)) return;
var tipo3Grezzo = String(r.Tipo3 || '').split(',').map(function(s) { return s.trim(); }).filter(Boolean);
var marker = L.marker([lat, lon], { icon: iconaPerLuogo(r.Tipo1, r.Tipo2, tipo3Grezzo) });
var link = 'https://www.ilcrepuscolodeglidei.org/index.php?title=' + encodeURIComponent(r.pagina.replace(/ /g, '_')) + (r.Ancora ? '#' + r.Ancora : '');
var sottotitolo = [r.Tipo1, r.Tipo2, r.Tipo3].filter(Boolean).join(' — ');
var popup = '<strong>' + r.Nome + '</strong>' +
(sottotitolo ? '<br><em>' + sottotitolo + '</em>' : '') +
(r.Descrizione ? '<br>' + r.Descrizione : '') +
'<br><a href="' + link + '" target="_blank">Vai alla voce: ' + r.pagina + '</a>';
marker.bindPopup(popup);
var tipo3PerFiltro = tipo3Grezzo.length ? tipo3Grezzo : ['']; // sentinella "(non specificato)", coerente col filtro
tuttiIMarkerLuoghi.push({ marker: marker, nome: String(r.Nome || '').trim(), popupBase: popup, tipo1: String(r.Tipo1 || '').trim(), tipo2: String(r.Tipo2 || '').trim(), tipo3: tipo3PerFiltro });
});
var distintiTipo1 = valoriDistinti(righe, 'Tipo1', ORDINE_TIPO1);
var distintiTipo2 = valoriDistinti(righe, 'Tipo2', null);
var distintiTipo3 = valoriDistintiMultivalore(righe, 'Tipo3');
filtroTipo1 = new Set(distintiTipo1); // tutto selezionato di default
filtroTipo2 = new Set(distintiTipo2);
filtroTipo3 = new Set(distintiTipo3);
costruisciPannelloFiltri(distintiTipo1, distintiTipo2, distintiTipo3);
applicaFiltroLuoghi();
}).catch(function(e) {
console.error('Errore caricando i Luoghi:', e);
});
// --- Avvio: carica geo.json + GeoConfiniIndex.json + Percorsi.json,
// poi mostra i Paesi. Le mitologie (solo da Mitologie.json, per
// ora — la fonte Cargo/Sezione resta definita ma disattivata,
// vedi caricaMitologie) si caricano in parallelo senza bloccare
// la mappa. ---
Promise.all([
caricaPaginaWikiJson('MediaWiki:geo.json'),
caricaPaginaWikiJson('MediaWiki:GeoConfiniIndex.json'),
caricaPaginaWikiJson('MediaWiki:Percorsi.json')
]).then(function(risultati) {
geoJsonDati = risultati[0] || {};
var indiceGrezzo = risultati[1] || {};
Object.keys(indiceGrezzo).forEach(function(k) {
var r = indiceGrezzo[k];
if (r && r.livello && r.nome) indiceConfini[r.livello + '|' + normalizza(r.nome)] = r;
});
percorsiDati = risultati[2] || {};
costruisciPannelloPercorsi();
mostraPaesi();
// Solo ora, perché espande i percorsi usando geoJsonDati (per le
// Regioni non indicate esplicitamente) — comunque senza bloccare
// la mappa, già visibile dalla riga sopra.
caricaMitologieCurate();
}).catch(function(e) {
cont.innerHTML = '<p style="color:#c00;">Errore caricando i dati della mappa: ' + e.message + '</p>';
});
}
});
} )( mediaWiki, jQuery );