Files
TakeID/content/content-script.js
T
Arthur Ria 6484dd09c5 v0.2
2026-05-20 09:47:25 +02:00

224 lines
6.8 KiB
JavaScript

(function () {
'use strict';
const browser = globalThis.browser || globalThis.chrome;
if (window.__takeid_content_script__) return;
window.__takeid_content_script__ = true;
// Cache des items recus du script injecte: [{index, id, code}]
let dataCache = [];
let lastCacheTimestamp = 0;
// ----- Injection de injected.js dans le contexte page -----
function injectPageScript() {
try {
const script = document.createElement('script');
script.src = browser.runtime.getURL('content/injected.js');
script.async = false;
script.onload = function () {
// Une fois execute, on retire l'element pour ne pas polluer le DOM
if (script.parentNode) script.parentNode.removeChild(script);
};
(document.head || document.documentElement).appendChild(script);
} catch (e) {
// swallow
}
}
injectPageScript();
// ----- Reception des donnees de injected.js -----
// Note: on ne filtre PAS sur event.source car sur Firefox le content script
// utilise un Xray wrapper et event.source !== window meme si c'est la meme page.
window.addEventListener('message', (event) => {
const data = event.data;
if (!data || typeof data !== 'object') return;
if (data.type !== 'TAKEID_DATA') return;
if (!Array.isArray(data.items)) return;
// Ne pas ecraser le cache avec une reponse vide (certains appels getDataViewList
// sont des requetes de count/metadata qui retournent datos=[])
if (data.items.length === 0) {
console.log('[TakeID] Ignoring empty getDataViewList response (keeping', dataCache.length, 'items in cache)');
return;
}
dataCache = data.items;
lastCacheTimestamp = data.timestamp || Date.now();
console.log('[TakeID] Cache updated:', dataCache.length, 'items');
});
// ----- Lecture du DOM : trouver les lignes selectionnees -----
function normalizeCode(s) {
if (s === null || s === undefined) return '';
return String(s).trim();
}
function findGridRoot() {
// Heuristique : chercher un conteneur de grille
const candidates = [
'.k-grid',
'[role="grid"]',
'table.grid',
'.smartui-grid',
'table'
];
for (const sel of candidates) {
const el = document.querySelector(sel);
if (el) return el;
}
return document;
}
function getAllDataRows(root) {
// Recupere toutes les lignes "data" (potentiellement tr ou divs)
let rows = root.querySelectorAll('tr[role="row"]');
if (rows && rows.length) return Array.from(rows);
rows = root.querySelectorAll('tbody > tr');
if (rows && rows.length) return Array.from(rows);
rows = root.querySelectorAll('[role="row"]');
if (rows && rows.length) return Array.from(rows);
rows = root.querySelectorAll('.k-grid-content tr');
if (rows && rows.length) return Array.from(rows);
return [];
}
function isHeaderRow(row) {
if (!row) return false;
if (row.matches && (row.matches('thead tr') || row.matches('[role="row"][aria-rowindex="1"]'))) return true;
// ligne sans cellules <td>, juste des <th>
if (row.querySelector && row.querySelector('th') && !row.querySelector('td')) return true;
return false;
}
function rowIsSelected(row) {
if (!row) return false;
// 1. classes communes
const selectedClasses = ['selected', 'k-state-selected', 'k-selected', 'is-selected', 'row-selected', 'active'];
if (row.classList) {
for (const cls of selectedClasses) {
if (row.classList.contains(cls)) return true;
}
}
// 2. aria-selected
if (row.getAttribute && row.getAttribute('aria-selected') === 'true') return true;
// 3. checkbox cochee a l'interieur de la ligne
const cb = row.querySelector && row.querySelector('input[type="checkbox"]');
if (cb && cb.checked) return true;
return false;
}
function extractCodeFromRow(row) {
if (!row) return null;
// 1. data-attribute eventuel
const dataAttrs = ['data-code', 'data-key', 'data-id'];
for (const a of dataAttrs) {
const v = row.getAttribute && row.getAttribute(a);
if (v) return normalizeCode(v);
}
// 2. premier lien clickable (souvent la colonne Code dans SmartUI)
const link = row.querySelector && row.querySelector('a');
if (link && link.textContent) {
const t = normalizeCode(link.textContent);
if (t) return t;
}
// 3. premiere cellule "non checkbox"
const cells = row.querySelectorAll ? row.querySelectorAll('td') : [];
for (const cell of cells) {
if (cell.querySelector('input[type="checkbox"]')) continue;
const t = normalizeCode(cell.textContent);
if (t) return t;
}
return null;
}
function indexOfDataRow(row, allRows) {
// Trouve l'index de la ligne dans la sequence des lignes "data" (hors header)
const dataRows = allRows.filter((r) => !isHeaderRow(r));
return dataRows.indexOf(row);
}
function getSelectedIds() {
const root = findGridRoot();
const allRows = getAllDataRows(root);
const dataRows = allRows.filter((r) => !isHeaderRow(r));
const selectedRows = dataRows.filter(rowIsSelected);
// Strategie A : trouver via le code (lookup dans le cache)
const cacheByCode = new Map();
for (const item of dataCache) {
if (item.code) cacheByCode.set(normalizeCode(item.code), item.id);
}
const pairs = [];
const seen = new Set();
for (const row of selectedRows) {
let id = null;
let resolvedCode = null;
const code = extractCodeFromRow(row);
if (code && cacheByCode.has(code)) {
id = cacheByCode.get(code);
resolvedCode = code;
}
// Strategie B : par index si le code n'a pas matche
if (!id) {
const idx = dataRows.indexOf(row);
if (idx >= 0 && idx < dataCache.length) {
id = dataCache[idx].id;
resolvedCode = dataCache[idx].code || code || null;
}
}
if (id && !seen.has(id)) {
seen.add(id);
pairs.push({ id, code: resolvedCode });
}
}
return {
pairs,
ids: pairs.map((p) => p.id),
selectedCount: selectedRows.length,
cacheSize: dataCache.length,
cacheTimestamp: lastCacheTimestamp
};
}
// ----- Reception des messages du popup -----
browser.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (!msg || typeof msg !== 'object') return false;
if (msg.type === 'TAKEID_PING') {
sendResponse({ ok: true, cacheSize: dataCache.length });
return false;
}
if (msg.type === 'GET_SELECTED_IDS') {
try {
const result = getSelectedIds();
sendResponse({ ok: true, ...result });
} catch (e) {
sendResponse({ ok: false, error: String(e && e.message ? e.message : e) });
}
return false;
}
return false;
});
})();