(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 , juste des 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; }); })();