Aggiorna la versione a 1.1.009 e aggiunge nuove funzionalità: pulsante per copiare il catalogo corsi in JSON, tool per la selezione della sede preferita, e miglioramenti nell'ordinamento dei pulsanti nella sidebar.
This commit is contained in:
parent
d23bee3a33
commit
8d88454abe
2 changed files with 285 additions and 40 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,2 +1,4 @@
|
|||
_ignored
|
||||
.venv
|
||||
.playwright-mcp
|
||||
.vscode
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
// @downloadURL https://luigidacunto.com/scripts/gaia-swissknife/tampermonkey-script/GAIA-Swissknife.user.js
|
||||
// @changelogURL https://luigidacunto.com/scripts/gaia-swissknife/tampermonkey-script/CHANGELOG.md
|
||||
// @author Luigi D'Acunto
|
||||
// @version 1.1.006
|
||||
// @version 1.1.009
|
||||
// @description Aggiunge funzionalità alle pagine di GAIA
|
||||
// @match https://gaia.cri.it/*
|
||||
// @require https://code.jquery.com/jquery-3.6.0.min.js
|
||||
|
|
@ -13,6 +13,8 @@
|
|||
// @resource DataTablesCSS https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css
|
||||
// @grant GM_addStyle
|
||||
// @grant GM_getResourceText
|
||||
// @grant GM_getValue
|
||||
// @grant GM_setValue
|
||||
// @run-at document-idle
|
||||
// ==/UserScript==
|
||||
|
||||
|
|
@ -29,18 +31,21 @@ GM_addStyle(GM_getResourceText("DataTablesCSS"));
|
|||
|
||||
// Inizializza le funzionalità
|
||||
|
||||
// Funzione per aggiungere il blocco di intestazione
|
||||
// Funzione per aggiungere il blocco di intestazione in cima alla sidebar
|
||||
function ensureSwissknifeHeader() {
|
||||
console.log("GAIA Swissknife: Aggiungo intestazione GAIA Swissknife...");
|
||||
if ($('#sezione').length > 0 && $('#gaia-swissknife-header').length === 0) {
|
||||
const headerHTML = `
|
||||
<hr>
|
||||
<li id="gaia-swissknife-header" class="dropdown-header grassetto piu-grande">
|
||||
GAIA Swissknife v${currentVersion}
|
||||
</li>
|
||||
`;
|
||||
$('#sezione').append(headerHTML);
|
||||
}
|
||||
if ($('#sezione').length === 0 || $('#gaia-swissknife-header').length > 0) return;
|
||||
console.log("GAIA Swissknife: Aggiungo intestazione GAIA Swissknife in cima...");
|
||||
const $header = $(`<li id="gaia-swissknife-header" class="dropdown-header grassetto piu-grande">GAIA Swissknife v${currentVersion}</li>`);
|
||||
const $divider = $('<hr id="gaia-swissknife-divider">');
|
||||
// Prepend: header prima, poi divider — risultato: [header][divider][voci esistenti]
|
||||
// I pulsanti vengono inseriti tra header e divider
|
||||
$('#sezione').prepend($divider).prepend($header);
|
||||
}
|
||||
|
||||
// Helper: inserisce un <li> con il pulsante tra l'header Swissknife e il divider
|
||||
function appendToSwissknifeSection($li) {
|
||||
ensureSwissknifeHeader();
|
||||
$('#gaia-swissknife-divider').before($li);
|
||||
}
|
||||
|
||||
// Funzione per correggere i filtri della sidebar delle autorizzazioni
|
||||
|
|
@ -231,26 +236,18 @@ GM_addStyle(GM_getResourceText("DataTablesCSS"));
|
|||
if ($('#sezione').length > 0) {
|
||||
console.log("GAIA Swissknife: Elementi trovati, aggiungo i pulsanti nel menu laterale...");
|
||||
|
||||
ensureSwissknifeHeader();
|
||||
// Aggiungi i pulsanti per settare gli esiti
|
||||
const buttonsHTML = `
|
||||
<li role="presentation">
|
||||
<button id="button_set_ampp" style="margin: 5px; padding: 8px; width: 97%; background-color: #007BFF; color: #FFF; border: none; border-radius: 4px; cursor: pointer;">
|
||||
Ammessi, Positivo, Positivo
|
||||
</button>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<button id="button_set_ampo" style="margin: 5px; padding: 8px; width: 97%; background-color: #28a745; color: #FFF; border: none; border-radius: 4px; cursor: pointer;">
|
||||
Ammessi, Positivo, Non previsto
|
||||
</button>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<button id="button_set_amop" style="margin: 5px; padding: 8px; width: 97%; background-color:rgb(167, 40, 99); color: #FFF; border: none; border-radius: 4px; cursor: pointer;">
|
||||
Ammessi, Non previsto, Positivo
|
||||
</button>
|
||||
</li>
|
||||
`;
|
||||
$('#sezione').append(buttonsHTML);
|
||||
[
|
||||
{ id: 'button_set_ampp', label: 'Ammessi, Positivo, Positivo', color: '#007BFF' },
|
||||
{ id: 'button_set_ampo', label: 'Ammessi, Positivo, Non previsto', color: '#28a745' },
|
||||
{ id: 'button_set_amop', label: 'Ammessi, Non previsto, Positivo', color: 'rgb(167, 40, 99)' }
|
||||
].forEach(({ id, label, color }) => {
|
||||
appendToSwissknifeSection(
|
||||
$('<li role="presentation"></li>').append(
|
||||
$(`<button id="${id}" type="button" style="margin:5px;padding:8px;width:97%;background-color:${color};color:#FFF;border:none;border-radius:4px;cursor:pointer;">${label}</button>`)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// Evento per il pulsante "Tutti Ammessi/Esito Positivo x2"
|
||||
$('#button_set_ampp').click(() => {
|
||||
|
|
@ -344,9 +341,7 @@ GM_addStyle(GM_getResourceText("DataTablesCSS"));
|
|||
|
||||
// Inserimento del pulsante nel menu laterale o in floating
|
||||
if ($('#sezione').length > 0) {
|
||||
ensureSwissknifeHeader();
|
||||
|
||||
$('#sezione').append(
|
||||
appendToSwissknifeSection(
|
||||
$('<li role="presentation"></li>').append(openButton)
|
||||
);
|
||||
} else {
|
||||
|
|
@ -585,6 +580,240 @@ GM_addStyle(GM_getResourceText("DataTablesCSS"));
|
|||
applyColoring();
|
||||
}
|
||||
|
||||
// Funzione per la selezione rapida del comitato preferito (persistente cross-sessione)
|
||||
function addSediPreferitaTool() {
|
||||
if ($('table tbody input[name="sedi"]').length === 0) return;
|
||||
|
||||
console.log("GAIA Swissknife: Aggiungo tool selezione sede preferita...");
|
||||
|
||||
// Costruisce la mappa di tutte le sedi dalla tabella
|
||||
function buildSediMap() {
|
||||
const map = [];
|
||||
document.querySelectorAll('table tbody tr').forEach(row => {
|
||||
const cb = row.querySelector('input[name="sedi"]');
|
||||
const nameTd = row.querySelector('td.grassetto');
|
||||
if (!cb || !nameTd) return;
|
||||
map.push({
|
||||
value: cb.value,
|
||||
label: nameTd.innerText.trim(),
|
||||
numCells: row.querySelectorAll('td').length
|
||||
});
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
const sediMap = buildSediMap();
|
||||
const savedValue = GM_getValue('preferred_sede_value', '');
|
||||
|
||||
// Select con indentazione visiva per la gerarchia
|
||||
const $select = $('<select id="gaia-preferred-sede-select"></select>').css({
|
||||
width: '100%', padding: '4px', margin: '4px 5px 4px 5px',
|
||||
fontSize: '12px', boxSizing: 'border-box', width: 'calc(100% - 10px)'
|
||||
});
|
||||
$select.append('<option value="">-- Comitato preferito --</option>');
|
||||
sediMap.forEach(({ value, label, numCells }) => {
|
||||
const prefix = numCells === 4 ? '' : numCells === 5 ? '\u00a0\u00a0\u00a0' : '\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0';
|
||||
const $opt = $(`<option value="${value}">${prefix}${label}</option>`);
|
||||
if (value === savedValue) $opt.prop('selected', true);
|
||||
$select.append($opt);
|
||||
});
|
||||
|
||||
$select.on('change', function () {
|
||||
GM_setValue('preferred_sede_value', $(this).val());
|
||||
});
|
||||
|
||||
// Pulsante: seleziona solo il preferito (+ figli se è Regionale o Locale)
|
||||
const $btnSolo = $('<button type="button" style="margin:2px 5px;padding:6px;width:calc(100% - 10px);background-color:#e67e00;color:#FFF;border:none;border-radius:4px;cursor:pointer;font-size:12px;">Solo preferito</button>');
|
||||
$btnSolo.on('click', function () {
|
||||
const val = $select.val();
|
||||
if (!val) { alert('Seleziona prima un comitato preferito.'); return; }
|
||||
|
||||
$('input[name="sedi"]').prop('checked', false);
|
||||
|
||||
const idx = sediMap.findIndex(s => s.value === val);
|
||||
if (idx === -1) return;
|
||||
|
||||
const selected = sediMap[idx];
|
||||
$(`#c_${selected.value}`).prop('checked', true);
|
||||
|
||||
// Se è Regionale (4) o Locale (5), seleziona anche tutti i figli
|
||||
if (selected.numCells <= 5) {
|
||||
for (let i = idx + 1; i < sediMap.length; i++) {
|
||||
if (sediMap[i].numCells <= selected.numCells) break;
|
||||
$(`#c_${sediMap[i].value}`).prop('checked', true);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`GAIA Swissknife: Selezionato solo "${selected.label}" e figli.`);
|
||||
});
|
||||
|
||||
// Pulsante: seleziona tutto
|
||||
const $btnAll = $('<button type="button" style="margin:2px 5px 6px 5px;padding:6px;width:calc(100% - 10px);background-color:#6c757d;color:#FFF;border:none;border-radius:4px;cursor:pointer;font-size:12px;">Seleziona tutto</button>');
|
||||
$btnAll.on('click', function () {
|
||||
$('input[name="sedi"]').prop('checked', true);
|
||||
console.log('GAIA Swissknife: Tutte le sedi selezionate.');
|
||||
});
|
||||
|
||||
appendToSwissknifeSection(
|
||||
$('<li role="presentation"></li>').append($select, $btnSolo, $btnAll)
|
||||
);
|
||||
}
|
||||
|
||||
// Funzione per copiare il catalogo corsi in clipboard come JSON
|
||||
function addCopyCatalogButton() {
|
||||
if (!window.location.href.includes('/courses/catalog')) return;
|
||||
if (!$('.corso-nome').length) return;
|
||||
|
||||
console.log("GAIA Swissknife: Aggiungo pulsante copia catalogo corsi...");
|
||||
|
||||
function extractCatalogo() {
|
||||
const main = document.querySelector('.col-md-9');
|
||||
if (!main) return [];
|
||||
const results = [];
|
||||
let currentTipo = '';
|
||||
for (const el of [...main.children]) {
|
||||
if (el.classList.contains('area-nome')) {
|
||||
currentTipo = el.textContent.trim();
|
||||
continue;
|
||||
}
|
||||
if (el.classList.contains('area-levels') && currentTipo) {
|
||||
let currentObiettivo = '';
|
||||
for (const child of [...el.children]) {
|
||||
if (child.classList.contains('area-nome')) {
|
||||
currentObiettivo = child.textContent.trim();
|
||||
} else if (child.classList.contains('area-levels') && currentObiettivo) {
|
||||
let currentLivello = '';
|
||||
for (const inner of [...child.children]) {
|
||||
if (inner.tagName === 'H4') {
|
||||
currentLivello = inner.textContent.trim();
|
||||
} else if (inner.tagName === 'SECTION') {
|
||||
const corsoNome = inner.querySelector('.corso-nome');
|
||||
if (!corsoNome) continue;
|
||||
const codice = corsoNome.querySelector('strong')?.textContent.trim() || '';
|
||||
const nome = corsoNome.textContent.trim().replace(/\s*\([^)]*\)\s*$/, '').trim();
|
||||
const scheda_pdf = inner.querySelector('.corso-scheda-completa a')?.href || null;
|
||||
results.push({ nome, codice, livello: currentLivello, tipo: currentTipo, obiettivo: currentObiettivo, scheda_pdf });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
const $btn = $('<button type="button" style="margin: 5px; padding: 8px; width: 97%; background-color: #c0504d; color: #FFF; border: none; border-radius: 4px; cursor: pointer;">Copia catalogo corsi (JSON)</button>');
|
||||
|
||||
$btn.on('click', function () {
|
||||
const catalogo = extractCatalogo();
|
||||
if (!catalogo.length) { alert('Nessun corso trovato.'); return; }
|
||||
|
||||
navigator.clipboard.writeText(JSON.stringify(catalogo, null, 2)).then(() => {
|
||||
console.log(`GAIA Swissknife: Catalogo copiato — ${catalogo.length} corsi.`);
|
||||
const orig = $(this).text();
|
||||
$(this).text(`✓ Copiato! (${catalogo.length} corsi)`).css('background-color', '#28a745');
|
||||
setTimeout(() => $(this).text(orig).css('background-color', '#c0504d'), 3000);
|
||||
}).catch(err => {
|
||||
console.error('GAIA Swissknife: Errore clipboard:', err);
|
||||
alert('Errore durante la copia in clipboard.');
|
||||
});
|
||||
});
|
||||
|
||||
if ($('#sezione').length > 0) {
|
||||
appendToSwissknifeSection($('<li role="presentation"></li>').append($btn));
|
||||
}
|
||||
}
|
||||
|
||||
// Funzione per estrarre la gerarchia sedi e copiarla in clipboard come JSON
|
||||
function addCopyStructureButton() {
|
||||
if (!window.location.href.includes('/us/elenchi/volontari/')) return;
|
||||
|
||||
// Il pulsante ha senso solo quando la tabella sedi è visibile (non nella vista risultati con iframe)
|
||||
if ($('table tbody tr').length === 0) return;
|
||||
|
||||
console.log("GAIA Swissknife: Aggiungo pulsante copia struttura sedi...");
|
||||
|
||||
function extractStructure() {
|
||||
const rows = document.querySelectorAll('table tbody tr');
|
||||
const regionali = [];
|
||||
let currentRegionale = null;
|
||||
let currentLocale = null;
|
||||
|
||||
rows.forEach(row => {
|
||||
const cells = row.querySelectorAll('td');
|
||||
const numCells = cells.length;
|
||||
|
||||
// Il nome è sempre nella cella con classe "grassetto"
|
||||
let nome = '';
|
||||
cells.forEach(td => {
|
||||
if (td.className.trim() === 'grassetto') {
|
||||
nome = td.innerText.trim();
|
||||
}
|
||||
});
|
||||
|
||||
if (!nome) return;
|
||||
|
||||
if (numCells === 4) {
|
||||
// Comitato Regionale
|
||||
currentRegionale = { nome, comitati_locali: [] };
|
||||
regionali.push(currentRegionale);
|
||||
currentLocale = null;
|
||||
} else if (numCells === 5) {
|
||||
// Comitato Locale
|
||||
currentLocale = { nome, sottosedi: [] };
|
||||
if (currentRegionale) {
|
||||
currentRegionale.comitati_locali.push(currentLocale);
|
||||
}
|
||||
} else if (numCells >= 6) {
|
||||
// Unità / Sottosede
|
||||
if (currentLocale) {
|
||||
currentLocale.sottosedi.push(nome);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Se c'è un solo regionale, restituisce l'oggetto direttamente (non array)
|
||||
return regionali.length === 1 ? regionali[0] : regionali;
|
||||
}
|
||||
|
||||
const $btn = $(`
|
||||
<button id="gaia-copy-structure-btn" type="button" style="margin: 5px; padding: 8px; width: 97%; background-color: #5a3d8a; color: #FFF; border: none; border-radius: 4px; cursor: pointer;">
|
||||
Copia struttura sedi (JSON)
|
||||
</button>
|
||||
`);
|
||||
|
||||
$btn.on('click', function () {
|
||||
const structure = extractStructure();
|
||||
const json = JSON.stringify(structure, null, 2);
|
||||
|
||||
navigator.clipboard.writeText(json).then(() => {
|
||||
const locali = Array.isArray(structure)
|
||||
? structure.reduce((acc, r) => acc + r.comitati_locali.length, 0)
|
||||
: structure.comitati_locali.length;
|
||||
const sottosedi = Array.isArray(structure)
|
||||
? structure.reduce((acc, r) => acc + r.comitati_locali.reduce((a, l) => a + l.sottosedi.length, 0), 0)
|
||||
: structure.comitati_locali.reduce((a, l) => a + l.sottosedi.length, 0);
|
||||
|
||||
console.log("GAIA Swissknife: JSON struttura sedi copiato in clipboard.");
|
||||
console.log(` Locali: ${locali}, Sottosedi: ${sottosedi}`);
|
||||
|
||||
const orig = $(this).text();
|
||||
$(this).text(`✓ Copiato! (${locali} locali, ${sottosedi} sottosedi)`).css('background-color', '#28a745');
|
||||
setTimeout(() => $(this).text(orig).css('background-color', '#5a3d8a'), 3000);
|
||||
}).catch(err => {
|
||||
console.error("GAIA Swissknife: Errore copia clipboard:", err);
|
||||
alert("Errore durante la copia in clipboard.");
|
||||
});
|
||||
});
|
||||
|
||||
// Inserisce in cima alla sidebar, nella sezione Swissknife
|
||||
if ($('#sezione').length > 0) {
|
||||
appendToSwissknifeSection(
|
||||
$('<li role="presentation"></li>').append($btn)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(() => {
|
||||
applyStyleFixes();
|
||||
addExamOutcomeButtons();
|
||||
|
|
@ -592,21 +821,35 @@ GM_addStyle(GM_getResourceText("DataTablesCSS"));
|
|||
addAuthorizationProfileTool();
|
||||
enhanceCurriculumTable();
|
||||
highlightRejectedCourses();
|
||||
addSediPreferitaTool();
|
||||
addCopyCatalogButton();
|
||||
addCopyStructureButton();
|
||||
console.log("GAIA Swissknife: Funzioni caricate con successo!");
|
||||
});
|
||||
|
||||
})(jQuery.noConflict(true));
|
||||
|
||||
// ==CHANGELOG==
|
||||
// v1.1.009 - Nuove funzioni:
|
||||
// - Aggiunto pulsante "Copia catalogo corsi (JSON)" nella sidebar di /courses/catalog/
|
||||
// - Estrae nome, codice, livello, tipo, obiettivo per ogni corso e copia il JSON in clipboard
|
||||
// - Aggiunto tool "Sede preferita" nelle pagine con selezione sedi: select persistente (GM_setValue/getValue),
|
||||
// pulsante "Solo preferito" (deseleziona tutto, seleziona il comitato scelto + figli), pulsante "Seleziona tutto"
|
||||
// - Pulsanti Swissknife ora posizionati in cima alla sidebar con header e separatore
|
||||
// v1.1.008 - Nuova funzione:
|
||||
// - Aggiunto pulsante "Copia struttura sedi (JSON)" nel menu laterale della pagina /us/elenchi/volontari/
|
||||
// - Estrae la gerarchia Comitato Regionale > Comitato Locale > Sottosedi dalla tabella sedi
|
||||
// - Copia il JSON in clipboard con feedback visivo; se presente un solo Regionale restituisce l'oggetto diretto, altrimenti un array
|
||||
// - Visibile solo quando la tabella sedi è presente (non nella vista risultati con iframe)
|
||||
// v1.0.4 - Fix:
|
||||
// - Modificata regex per il matching dei codici corso, compatibile con i codici che contengono caratteri alfanumerici
|
||||
// v1.0.2 - Fix:
|
||||
// - Modificata regex per il matching dei codici corso, compatibile con i codici che contengono spazi
|
||||
// - Aggiunto link per aprire la pagina del corso nella modale dove compaiono i corsi rilevati
|
||||
// v1.0.0 - Versione stabile, con:
|
||||
// - DataTable integrata nella tabella curriculum
|
||||
// - Evidenziazione "Respinto"
|
||||
// - Correzioni grafiche file/link
|
||||
// - Gestione richieste iscrizioni con modale mobile
|
||||
// - Funzioni esiti massivi su termina corso
|
||||
// v1.0.2 - Fix:
|
||||
// - Modificata regex per il matching dei codici corso, compatibile con i codici che contengono spazi
|
||||
// - Aggiunto link per aprire la pagina del corso nella modale dove compaiono i corsi rilevati
|
||||
// v1.0.4 - Fix:
|
||||
// - Modificata regex per il matching dei codici corso, compatibile con i codici che contengono caratteri alfanumerici
|
||||
// ==/CHANGELOG==
|
||||
Loading…
Add table
Add a link
Reference in a new issue