923 lines
No EOL
42 KiB
JavaScript
923 lines
No EOL
42 KiB
JavaScript
// ==UserScript==
|
||
// @name GAIA - Swissknife
|
||
// @namespace https://git.luigidacunto.com/cri/gaia-swissknife.git
|
||
// @updateURL https://luigidacunto.com/scripts/gaia-swissknife/tampermonkey-script/GAIA-Swissknife.user.js
|
||
// @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.011
|
||
// @description Aggiunge funzionalità alle pagine di GAIA
|
||
// @match https://gaia.cri.it/*
|
||
// @require https://code.jquery.com/jquery-3.6.0.min.js
|
||
// @require https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js
|
||
// @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==
|
||
|
||
GM_addStyle(GM_getResourceText("DataTablesCSS"));
|
||
|
||
(function($) {
|
||
'use strict';
|
||
console.log("GAIA Swissknife: Inizializzazione in corso!");
|
||
|
||
// Recupera la versione dallo script se disponibile, altrimenti usa un valore di fallback
|
||
const currentVersion = (typeof GM_info !== 'undefined' && GM_info.script && GM_info.script.version) ? GM_info.script.version : 'x.x';
|
||
|
||
console.log(`GAIA Swissknife: Versione ${currentVersion}`);
|
||
|
||
// Inizializza le funzionalità
|
||
|
||
// Funzione per aggiungere il blocco di intestazione in cima alla sidebar
|
||
function ensureSwissknifeHeader() {
|
||
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
|
||
function fixAutorizzazioniSidebarFilters() {
|
||
const $ul = $("#sezione-2");
|
||
if (!$ul.length) return;
|
||
|
||
const voci = {};
|
||
|
||
// Scansiona tutti i link
|
||
$ul.find("li[role='presentation'] a").each(function () {
|
||
const $a = $(this);
|
||
const text = $a.clone().children().remove().end().text().trim();
|
||
const badge = parseInt($a.find(".badge").text().trim(), 10);
|
||
const href = $a.attr("href");
|
||
|
||
if (!text || isNaN(badge)) return;
|
||
|
||
if (!voci[text]) {
|
||
voci[text] = {
|
||
count: 0,
|
||
href: href
|
||
};
|
||
}
|
||
|
||
voci[text].count += badge;
|
||
});
|
||
|
||
$ul.find("li[role='presentation']").remove();
|
||
|
||
Object.entries(voci).forEach(([tipo, info]) => {
|
||
const $li = $(`
|
||
<li role="presentation">
|
||
<a href="${info.href}">
|
||
${tipo}
|
||
<span class="badge pull-right">${info.count}</span>
|
||
</a>
|
||
</li>
|
||
`);
|
||
$ul.append($li);
|
||
});
|
||
}
|
||
// Funzione per ricostruire le celle con i dettagli delle qualifiche nel curriculum in modo pulito
|
||
function rebuildCurriculumDetailCells() {
|
||
$("td.piu-piccolo").each(function (cellIndex) {
|
||
const $td = $(this);
|
||
const rawNodes = Array.from(this.childNodes);
|
||
|
||
const nodes = rawNodes.filter(n => {
|
||
if (n.nodeType === 3) return n.nodeValue.trim() !== "";
|
||
return true;
|
||
});
|
||
|
||
console.log(`\n📦 [Cella ${cellIndex + 1}] Analizzo ${nodes.length} nodi validi...`);
|
||
const newContent = $('<div></div>');
|
||
|
||
for (let i = 0; i < nodes.length; i++) {
|
||
const node = nodes[i];
|
||
|
||
if (node.nodeType === 1 && node.tagName === "I") {
|
||
const $icon = $(node).clone();
|
||
const $row = $('<div class="gaia-line"></div>').css({
|
||
"display": "flex",
|
||
"align-items": "baseline",
|
||
"gap": "5px",
|
||
"line-height": "1.4",
|
||
"margin-bottom": "2px"
|
||
});
|
||
|
||
$icon.css({
|
||
"margin-right": "4px",
|
||
"vertical-align": "middle"
|
||
});
|
||
|
||
$row.append($icon);
|
||
|
||
let content = null;
|
||
let nextIndex = i + 1;
|
||
|
||
while (nextIndex < nodes.length) {
|
||
const nextNode = nodes[nextIndex];
|
||
|
||
if (nextNode.nodeType === 3) {
|
||
const text = nextNode.nodeValue.trim();
|
||
if (text) {
|
||
content = $('<span></span>').text(text);
|
||
if (text.length > 40) {
|
||
content.css({
|
||
"display": "inline-block",
|
||
"max-width": "200px",
|
||
"white-space": "nowrap",
|
||
"overflow": "hidden",
|
||
"text-overflow": "ellipsis",
|
||
"vertical-align": "middle"
|
||
}).attr("title", text);
|
||
}
|
||
break;
|
||
}
|
||
} else if (nextNode.nodeType === 1) {
|
||
const $el = $(nextNode);
|
||
|
||
if (nextNode.tagName === "A") {
|
||
let text = $el.text().trim();
|
||
|
||
if (!text) {
|
||
const href = $el.attr("href") || "";
|
||
const fallback = href.split("/").pop();
|
||
text = fallback || "(documento)";
|
||
$el.text(text);
|
||
}
|
||
|
||
$el.css({
|
||
"display": "inline-block",
|
||
"max-width": "200px",
|
||
"white-space": "nowrap",
|
||
"overflow": "hidden",
|
||
"text-overflow": "ellipsis",
|
||
"vertical-align": "middle",
|
||
"color": "#a94442"
|
||
}).attr("title", text);
|
||
|
||
content = $el;
|
||
break;
|
||
}
|
||
|
||
else if (nextNode.tagName === "SPAN") {
|
||
const text = $el.text().trim();
|
||
if (text) {
|
||
content = $el.clone();
|
||
|
||
content.css({
|
||
"display": "inline-block",
|
||
"max-width": "200px",
|
||
"white-space": "nowrap",
|
||
"overflow": "hidden",
|
||
"text-overflow": "ellipsis",
|
||
"vertical-align": "middle"
|
||
}).attr("title", text);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
nextIndex++;
|
||
}
|
||
|
||
i = nextIndex - 1;
|
||
|
||
if (content) {
|
||
$row.append(content);
|
||
newContent.append($row);
|
||
console.log(`✅ Riga generata: ${$icon.attr("class")} + contenuto`);
|
||
} else {
|
||
console.warn(`⚠️ Riga ignorata: icona "${$icon.attr("class")}" senza contenuto`);
|
||
}
|
||
}
|
||
}
|
||
|
||
$td.empty().append(newContent);
|
||
});
|
||
}
|
||
|
||
|
||
// Funzione per applicare le correzioni di stile alle celle del curriculum
|
||
function applyStyleFixes() {
|
||
const url = window.location.href;
|
||
|
||
// Correzione per le celle del curriculum
|
||
if (url.match(/\/profilo\/\d+\/curriculum\//)) {
|
||
console.log("GAIA Swissknife: Ricostruzione celle curriculum in modo ordinato...");
|
||
rebuildCurriculumDetailCells();
|
||
}
|
||
|
||
// Correzione per menu laterale autorizzazioni
|
||
if (url.includes("/autorizzazioni")) {
|
||
console.log("GAIA Swissknife: Correzione menu laterale autorizzazioni...");
|
||
fixAutorizzazioniSidebarFilters();
|
||
}
|
||
|
||
}
|
||
|
||
// Funzione per aggiungere i pulsanti per settare gli esiti degli esami
|
||
function addExamOutcomeButtons() {
|
||
console.log("GAIA Swissknife: esecuzione di addExamOutcomeButtons() ...");
|
||
// Verifica che ci siano i select che gestiscono gli esiti
|
||
if ($("select[id^='id_part_'][id$='-ammissione']").length > 0) {
|
||
// Cerca il menu laterale (ul#sezione) dove inserire il blocco
|
||
if ($('#sezione').length > 0) {
|
||
console.log("GAIA Swissknife: Elementi trovati, aggiungo i pulsanti nel menu laterale...");
|
||
|
||
// Aggiungi i pulsanti per settare gli esiti
|
||
[
|
||
{ 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(() => {
|
||
$("select[id^='id_part_'][id$='-ammissione']").val("AM").trigger('change');
|
||
$("select[id^='id_part_'][id$='-esito_parte_1']").val("P").trigger('change');
|
||
$("select[id^='id_part_'][id$='-esito_parte_2']").val("P").trigger('change');
|
||
console.log("GAIA Swissknife: Valori impostati - Ammessi, Positivo, Positivo");
|
||
});
|
||
|
||
// Evento per il pulsante "Ammessi, Positivo, Non previsto"
|
||
$('#button_set_ampo').click(() => {
|
||
$("select[id^='id_part_'][id$='-ammissione']").val("AM").trigger('change');
|
||
$("select[id^='id_part_'][id$='-esito_parte_1']").val("P").trigger('change');
|
||
$("select[id^='id_part_'][id$='-esito_parte_2']").val("O").trigger('change');
|
||
console.log("GAIA Swissknife: Valori impostati - Ammessi, Positivo, Non previsto");
|
||
});
|
||
|
||
// Evento per il pulsante "Ammessi, Non previsto, Positivo"
|
||
$('#button_set_amop').click(() => {
|
||
$("select[id^='id_part_'][id$='-ammissione']").val("AM").trigger('change');
|
||
$("select[id^='id_part_'][id$='-esito_parte_1']").val("O").trigger('change');
|
||
$("select[id^='id_part_'][id$='-esito_parte_2']").val("P").trigger('change');
|
||
console.log("GAIA Swissknife: Valori impostati - Ammessi, Non previsto, Positivo");
|
||
});
|
||
|
||
} else {
|
||
console.log("GAIA Swissknife: Elemento ul#sezione non trovato.");
|
||
}
|
||
} else {
|
||
console.log("GAIA Swissknife: Nessun elemento trovato, i pulsanti non verranno mostrati.");
|
||
}
|
||
}
|
||
|
||
// Funzione per aggiungere il pulsante "Apri tutti i Curriculum" sezione iscritti di un corso
|
||
function addOpenAllProfilesButton() {
|
||
if (!window.location.href.includes('/iscritti/')) return;
|
||
|
||
const openButton = $(`
|
||
<button id="open_all_profiles" style="margin: 5px; padding: 8px; width: 97%; background-color:rgba(234, 0, 255, 0.65); color: #FFF; border: none; border-radius: 4px; cursor: pointer;">
|
||
Apri tutti i Curriculum
|
||
</button>
|
||
`);
|
||
|
||
openButton.on('click', () => {
|
||
const iframe = document.querySelector('iframe.embed-responsive-item');
|
||
if (!iframe) {
|
||
alert("Impossibile trovare l'iframe con la lista.");
|
||
console.log("GAIA Swissknife: iframe non trovato.");
|
||
return;
|
||
}
|
||
|
||
// Verifica che il contenuto sia accessibile (same-origin)
|
||
let iframeDoc;
|
||
try {
|
||
iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||
} catch (err) {
|
||
alert("Accesso all'iframe bloccato per motivi di sicurezza (cross-origin).");
|
||
console.error("GAIA Swissknife: accesso iframe negato.", err);
|
||
return;
|
||
}
|
||
|
||
const $iframeLinks = $(iframeDoc).find("a[href^='/profilo/'][href*='?fpc']");
|
||
if ($iframeLinks.length === 0) {
|
||
alert("Nessun profilo trovato all'interno dell'iframe.");
|
||
console.log("GAIA Swissknife: Nessun link profilo nell'iframe.");
|
||
return;
|
||
}
|
||
|
||
const profileLinks = $iframeLinks.map(function() {
|
||
const href = $(this).attr('href');
|
||
const match = href.match(/\/profilo\/(\d+)\//);
|
||
if (match && match[1]) {
|
||
const id = match[1];
|
||
return `https://gaia.cri.it/profilo/${id}/curriculum/?us`;
|
||
}
|
||
return null;
|
||
}).get().filter(Boolean);
|
||
|
||
// Mostra le URL in console sempre
|
||
console.log("GAIA Swissknife: Profili trovati:");
|
||
profileLinks.forEach(link => console.log(link));
|
||
|
||
if (!confirm(`Vuoi aprire ${profileLinks.length} profili in nuove schede?`)) return;
|
||
|
||
profileLinks.forEach(link => {
|
||
window.open(link, '_blank');
|
||
});
|
||
|
||
console.log(`GAIA Swissknife: Aperte ${profileLinks.length} schede da iframe.`);
|
||
});
|
||
|
||
// Inserimento del pulsante nel menu laterale o in floating
|
||
if ($('#sezione').length > 0) {
|
||
appendToSwissknifeSection(
|
||
$('<li role="presentation"></li>').append(openButton)
|
||
);
|
||
} else {
|
||
openButton.css({
|
||
position: 'fixed',
|
||
top: '10px',
|
||
right: '10px',
|
||
zIndex: 9999
|
||
}).appendTo('body');
|
||
}
|
||
}
|
||
|
||
// Funzione per evidenziare le righe con "Respinto"
|
||
function highlightRejectedCourses() {
|
||
if (!window.location.href.match(/\/profilo\/\d+\/curriculum\//)) return;
|
||
|
||
console.log("GAIA Swissknife: Avvio evidenziazione 'Respinto'…");
|
||
|
||
const $table = $(".table.table-striped");
|
||
if (!$table.length || !$.fn.DataTable.isDataTable($table)) return;
|
||
|
||
const api = $table.DataTable();
|
||
|
||
function applyRejectionStyle() {
|
||
// Prima puliamo tutte le righe (reset)
|
||
api.rows().every(function () {
|
||
const $row = $(this.node());
|
||
$row.css({
|
||
"box-shadow": "",
|
||
"background-color": ""
|
||
});
|
||
});
|
||
|
||
// Poi coloriamo solo le righe che contengono <span class="text-danger">Respinto</span>
|
||
$table.find("span.text-danger").each(function () {
|
||
const text = $(this).text().trim().toLowerCase();
|
||
if (text === "respinto") {
|
||
const $row = $(this).closest("tr");
|
||
console.log("🟥 Evidenzio riga:", $row.index(), $row.find("p.grassetto").text().trim());
|
||
$row.css({
|
||
"box-shadow": "inset 8px 0 0 0 #dc3545",
|
||
"background-color": "#fbe9ea"
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
applyRejectionStyle();
|
||
$table.on("draw.dt", applyRejectionStyle);
|
||
}
|
||
|
||
// Funzione per aggiungere il tool per i profili delle richieste iscrizioni
|
||
function addAuthorizationProfileTool() {
|
||
if (!window.location.href.includes("/autorizzazioni/")) return;
|
||
|
||
console.log("GAIA Swissknife: Attivo tool per gestione richieste iscrizione...");
|
||
|
||
const richieste = [];
|
||
|
||
$(".panel.panel-default").each(function () {
|
||
const $panel = $(this);
|
||
const $corsoLink = $panel.find("a[href^='/aspirante/corso-base/']");
|
||
|
||
if ($corsoLink.length === 0) return; // blocco non relativo a richiesta corso
|
||
|
||
const corsoText = $corsoLink.text().trim();
|
||
const match = corsoText.match(/([A-Z]{2,}\/\d{4}\/[A-Z0-9\-]+(?: [A-Z0-9\-]+)?\/\d+)/);
|
||
if (!match) return;
|
||
|
||
const codiceCorso = match[1];
|
||
const $profileLink = $panel.find("a[href^='/profilo/']").first();
|
||
const nome = $profileLink.text().trim();
|
||
const href = $profileLink.attr("href");
|
||
const idMatch = href.match(/\/profilo\/(\d+)\//);
|
||
|
||
if (idMatch && idMatch[1]) {
|
||
richieste.push({
|
||
codice: codiceCorso,
|
||
nome,
|
||
id: idMatch[1],
|
||
linkCorso: $corsoLink.attr("href"),
|
||
$panel
|
||
});
|
||
}
|
||
});
|
||
|
||
const corsiUnici = [...new Set(richieste.map(r => r.codice))];
|
||
if (corsiUnici.length === 0) return;
|
||
|
||
// === CREAZIONE MODALE ===
|
||
const $modal = $(`
|
||
<div id="gaia-auth-tool" style="position: fixed; top: 20px; right: 20px; width: 320px; background: white; border: 2px solid #007BFF; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); z-index: 9999; overflow: hidden;">
|
||
<div id="gaia-auth-header" style="background-color: #007BFF; color: white; padding: 8px 10px; font-weight: bold; cursor: move; display: flex; justify-content: space-between; align-items: center;">
|
||
<span>🛠 GAIA Swissknife – Richieste iscrizione</span>
|
||
<button id="gaia-auth-toggle" title="Riduci/Espandi" style="background: transparent; border: none; color: white; font-weight: bold; font-size: 16px; cursor: pointer; padding: 0; margin-left: 10px;">−</button>
|
||
</div>
|
||
<div id="gaia-auth-body" style="padding: 15px;">
|
||
<div id="gaia-auth-summary" style="margin-bottom: 10px; font-size: 13px;">
|
||
<strong>📋 Richieste trovate:</strong><br>
|
||
${corsiUnici.map(c => {
|
||
const matching = richieste.filter(r => r.codice === c);
|
||
const count = matching.length;
|
||
const plural = count === 1 ? "richiesta" : "richieste";
|
||
const courseUrl = matching[0]?.linkCorso ? `https://gaia.cri.it${matching[0].linkCorso}` : null;
|
||
const label = courseUrl
|
||
? `<a href="${courseUrl}" target="_blank">${c}</a> – ${count} ${plural}`
|
||
: `${c} – ${count} ${plural}`;
|
||
return `• ${label}`;
|
||
}).join("<br>")}
|
||
</div>
|
||
<label for="gaia-auth-select">Seleziona un corso:</label>
|
||
<select id="gaia-auth-select" style="width: 100%; padding: 5px; margin-bottom: 10px;">
|
||
<option value="">-- Seleziona --</option>
|
||
${corsiUnici.map(c => `<option value="${c}">${c}</option>`).join("")}
|
||
</select>
|
||
<button id="gaia-auth-btn" style="width: 100%; padding: 8px; background-color: #007BFF; color: white; border: none; border-radius: 4px; cursor: pointer;">Recupera Profili</button>
|
||
<div id="gaia-auth-openall-container" style="display: none; margin-top: 10px;">
|
||
<button id="gaia-auth-openall" style="width: 100%; padding: 6px; background-color: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer;">Apri tutti i Curriculum</button>
|
||
</div>
|
||
<div id="gaia-auth-results" style="margin-top: 10px; max-height: 200px; overflow-y: auto; border-top: 1px solid #ddd; padding-top: 10px;"></div>
|
||
</div>
|
||
</div>
|
||
`);
|
||
|
||
$('body').append($modal);
|
||
|
||
// Riduci / Espandi
|
||
let isCollapsed = false;
|
||
$('#gaia-auth-toggle').on('click', function (e) {
|
||
isCollapsed = !isCollapsed;
|
||
$('#gaia-auth-body').slideToggle(200);
|
||
$(this).text(isCollapsed ? '+' : '−');
|
||
e.stopPropagation();
|
||
});
|
||
|
||
// Modal mobile
|
||
let isDragging = false, dragOffsetX = 0, dragOffsetY = 0;
|
||
$('#gaia-auth-header').on('mousedown', function (e) {
|
||
const rect = document.getElementById('gaia-auth-tool').getBoundingClientRect();
|
||
isDragging = true;
|
||
dragOffsetX = e.clientX - rect.left;
|
||
dragOffsetY = e.clientY - rect.top;
|
||
e.preventDefault();
|
||
});
|
||
$(document).on('mousemove', function (e) {
|
||
if (!isDragging) return;
|
||
const $modal = $('#gaia-auth-tool');
|
||
let left = e.clientX - dragOffsetX;
|
||
let top = e.clientY - dragOffsetY;
|
||
left = Math.max(0, Math.min(left, window.innerWidth - $modal.outerWidth()));
|
||
top = Math.max(0, Math.min(top, window.innerHeight - $modal.outerHeight()));
|
||
$modal.css({ left: left + 'px', top: top + 'px', right: 'auto' });
|
||
}).on('mouseup', () => isDragging = false);
|
||
|
||
// Recupera profili
|
||
$('#gaia-auth-btn').on('click', function () {
|
||
const codiceSelezionato = $('#gaia-auth-select').val();
|
||
const $results = $('#gaia-auth-results').empty();
|
||
$('#gaia-auth-openall-container').hide();
|
||
|
||
if (!codiceSelezionato) {
|
||
alert("Seleziona un codice corso.");
|
||
return;
|
||
}
|
||
|
||
// Reset evidenziazione
|
||
$(".panel.panel-default").css("background-color", "");
|
||
|
||
const trovati = richieste.filter(r => r.codice === codiceSelezionato);
|
||
if (trovati.length === 0) {
|
||
$results.append(`<div style="color: red;">Nessun risultato trovato.</div>`);
|
||
return;
|
||
}
|
||
|
||
trovati.forEach(r => {
|
||
r.$panel.css("background-color", "#d4edda");
|
||
const link = `https://gaia.cri.it/profilo/${r.id}/curriculum/?us`;
|
||
$results.append(`<div><a href="${link}" target="_blank">${r.nome}</a></div>`);
|
||
});
|
||
|
||
$('#gaia-auth-openall-container').show();
|
||
$('#gaia-auth-openall').off('click').on('click', function () {
|
||
trovati.forEach(r => window.open(`https://gaia.cri.it/profilo/${r.id}/curriculum/?us`, '_blank'));
|
||
});
|
||
});
|
||
}
|
||
|
||
// Funzione per migliorare la tabella del curriculum
|
||
function enhanceCurriculumTable() {
|
||
if (!window.location.href.match(/\/profilo\/\d+\/curriculum\//)) return;
|
||
|
||
const palette = {
|
||
"Qualifica CRI": "#e6f0ff", // blu chiaro
|
||
"Altra Qualifica": "#e6fff0", // verde chiaro
|
||
"Esperienze Professionali": "#fff3e6", // arancione chiaro
|
||
"Competenza Personale": "#e6faff", // ciano chiaro
|
||
"Patente Civile": "#f0f0f0", // grigio chiaro
|
||
"Patente CRI": "#f3e6ff" // lilla chiaro
|
||
};
|
||
|
||
const $table = $(".table.table-striped");
|
||
const $tbody = $table.find("tbody");
|
||
if (!$table.length || !$tbody.length) return;
|
||
|
||
console.log("GAIA Swissknife: DataTable + Colorazione permanente per tipologia...");
|
||
|
||
$table.DataTable({
|
||
paging: false,
|
||
order: [[0, 'asc'], [1, 'asc']],
|
||
columnDefs: [
|
||
{ orderable: false, targets: 2 }
|
||
],
|
||
language: {
|
||
search: "Filtra qualifiche:",
|
||
zeroRecords: "Nessuna qualifica trovata",
|
||
info: "Mostrate _TOTAL_ qualifiche",
|
||
infoEmpty: "Nessuna qualifica disponibile",
|
||
infoFiltered: "(filtrate da _MAX_ totali)"
|
||
}
|
||
});
|
||
|
||
function applyColoring() {
|
||
const tableApi = $table.DataTable();
|
||
tableApi.rows().every(function () {
|
||
const $row = $(this.node());
|
||
const $tipoCell = $row.find("td").eq(0);
|
||
const tipo = $tipoCell.text().trim();
|
||
|
||
if (palette[tipo]) {
|
||
$tipoCell.css("background-color", palette[tipo]);
|
||
}
|
||
});
|
||
}
|
||
|
||
// Applica le correzioni le colorazioni dei tipi di qualifica in lista
|
||
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)
|
||
);
|
||
}
|
||
}
|
||
|
||
// Converte una data in italiano (es. "Domenica 27 Settembre 2026 09:00") in formato ISO 8601 ("2026-09-27T09:00:00")
|
||
function parseDataItaliana(testo) {
|
||
const mesi = { gennaio: 1, febbraio: 2, marzo: 3, aprile: 4, maggio: 5, giugno: 6, luglio: 7, agosto: 8, settembre: 9, ottobre: 10, novembre: 11, dicembre: 12 };
|
||
const m = (testo || '').match(/(\d{1,2})\s+([a-zA-Zàèìòù]+)\s+(\d{4})\s+(\d{2}):(\d{2})/i);
|
||
if (!m) return null;
|
||
const mese = mesi[m[2].toLowerCase()];
|
||
if (!mese) return null;
|
||
const giorno = m[1].padStart(2, '0');
|
||
return `${m[3]}-${String(mese).padStart(2, '0')}-${giorno}T${m[4]}:${m[5]}:00`;
|
||
}
|
||
|
||
// Funzione per copiare i dati del corso per LURCH in clipboard come JSON
|
||
function addCopyForLurchButton() {
|
||
if (!window.location.href.match(/\/aspirante\/corso-base\/\d+\//)) return;
|
||
if ($('#sezione').length === 0) return;
|
||
|
||
console.log("GAIA Swissknife: Aggiungo pulsante Copia per LURCH...");
|
||
|
||
function getPanel(labelSubstr) {
|
||
return [...document.querySelectorAll('.panel.panel-info')].find(el => el.querySelector('.panel-title')?.innerText.includes(labelSubstr));
|
||
}
|
||
function getPanelText(labelSubstr) {
|
||
return getPanel(labelSubstr)?.querySelector('.panel-body')?.innerText?.trim() || '';
|
||
}
|
||
|
||
function extractLurchData() {
|
||
const corso = document.querySelector('h1, h2')?.innerText?.trim() || '';
|
||
|
||
const titleMatch = document.title.match(/([A-Z]{2,}\/\d{4}\/[A-Z0-9\-]+(?: [A-Z0-9\-]+)?\/\d+)/);
|
||
const protocollo = titleMatch ? titleMatch[1] : '';
|
||
|
||
const direttore = getPanel('Direttore')?.querySelector('.panel-body a')?.innerText?.trim() || '';
|
||
|
||
const quotaText = getPanelText('Quota') || '0';
|
||
const quotaValore = parseFloat(quotaText.replace(/[^\d,]/g, '').replace(',', '.')) || 0;
|
||
const quota = quotaValore !== 0;
|
||
|
||
const dataInizio = parseDataItaliana(getPanelText('Data di inizio'));
|
||
const dataEsame = parseDataItaliana(getPanelText('Data di esame'));
|
||
const scadenzaDomande = parseDataItaliana(getPanelText('Termine di scadenza per le domande di ammissione'));
|
||
|
||
return { direttore, quota, quotaValore, corso, protocollo, linkGaia: window.location.href, dataInizio, dataEsame, scadenzaDomande };
|
||
}
|
||
|
||
const $btn = $('<button type="button" style="margin: 5px; padding: 8px; width: 97%; background-color: #1a6b8a; color: #FFF; border: none; border-radius: 4px; cursor: pointer;">Copia per LURCH (JSON)</button>');
|
||
|
||
$btn.on('click', function () {
|
||
const data = extractLurchData();
|
||
navigator.clipboard.writeText(JSON.stringify(data, null, 2)).then(() => {
|
||
console.log('GAIA Swissknife: Dati LURCH copiati in clipboard.', data);
|
||
const orig = $(this).text();
|
||
$(this).text('✓ Copiato!').css('background-color', '#28a745');
|
||
setTimeout(() => $(this).text(orig).css('background-color', '#1a6b8a'), 3000);
|
||
}).catch(err => {
|
||
console.error('GAIA Swissknife: Errore clipboard:', err);
|
||
alert('Errore durante la copia in clipboard.');
|
||
});
|
||
});
|
||
|
||
appendToSwissknifeSection($('<li role="presentation"></li>').append($btn));
|
||
}
|
||
|
||
$(document).ready(() => {
|
||
applyStyleFixes();
|
||
addExamOutcomeButtons();
|
||
addOpenAllProfilesButton();
|
||
addAuthorizationProfileTool();
|
||
enhanceCurriculumTable();
|
||
highlightRejectedCourses();
|
||
addSediPreferitaTool();
|
||
addCopyCatalogButton();
|
||
addCopyStructureButton();
|
||
addCopyForLurchButton();
|
||
console.log("GAIA Swissknife: Funzioni caricate con successo!");
|
||
});
|
||
|
||
})(jQuery.noConflict(true));
|
||
|
||
// ==CHANGELOG==
|
||
// v1.1.011 - Nuovi campi nel JSON "Copia per LURCH":
|
||
// - Aggiunti dataInizio, dataEsame e scadenzaDomande (termine per le domande di ammissione), in formato ISO 8601
|
||
// v1.1.010 - Nuova funzione:
|
||
// - Aggiunto pulsante "Copia per LURCH (JSON)" nella sidebar delle pagine corso (/aspirante/corso-base/)
|
||
// - Estrae direttore, quota, corso, protocollo e link GAIA, e copia il JSON in clipboard
|
||
// 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
|
||
// ==/CHANGELOG==
|