您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Módulo para importar y exportar datos en formato XML para WME Place Normalizer. No funciona por sí solo.
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyforks.org/scripts/548745/1656811/WME%20PLN%20Core%20-%20XML%20Handler.js
// ==UserScript== // @name WME PLN Core - XML Handler // @namespace https://greasyforks.org/en/users/mincho77 // @version 9.0.0 // @description Módulo para importar y exportar datos en formato XML para WME Place Normalizer. No funciona por sí solo. // @author mincho77 // @license MIT // @grant none // ==/UserScript== function exportSharedDataToXml(type = "full") { try { const _excludedWords = (window.excludedWords instanceof Set) ? window.excludedWords : new Set(Array.isArray(window.excludedWords) ? window.excludedWords : []); const _replacementWords = (window.replacementWords && typeof window.replacementWords === 'object') ? window.replacementWords : {}; const _swapWords = Array.isArray(window.swapWords) ? window.swapWords : []; const _editorStats = (window.editorStats && typeof window.editorStats === 'object') ? window.editorStats : {}; const _excludedPlaces = (window.excludedPlaces instanceof Map) ? window.excludedPlaces : new Map(); const _processedPlaces = Array.isArray(window.processedPlaces) ? window.processedPlaces : []; let xmlParts = []; const rootTagName = "WME_PLN_Backup"; const fileName = "wme_pln_full_backup.xml"; if (_excludedWords.size === 0 && Object.keys(_replacementWords).length === 0 && _swapWords.length === 0 && Object.keys(_editorStats).length === 0 && _excludedPlaces.size === 0 && _processedPlaces.length === 0) { alert("No hay datos para exportar."); return; } if (_excludedWords.size > 0) { xmlParts.push(" <words>"); Array.from(_excludedWords).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())).forEach(w => xmlParts.push(` <word>${xmlEscape(w)}</word>`)); xmlParts.push(" </words>"); } if (Object.keys(_replacementWords).length > 0) { xmlParts.push(" <replacements>"); Object.entries(_replacementWords).sort((a, b) => a[0].toLowerCase().localeCompare(b[0].toLowerCase())).forEach(([from, to]) => { xmlParts.push(` <replacement from="${xmlEscape(from)}">${xmlEscape(to)}</replacement>`); }); xmlParts.push(" </replacements>"); } if (_swapWords.length > 0) { xmlParts.push(" <swapWords>"); _swapWords.forEach(item => { xmlParts.push(` <swap value="${xmlEscape(item.word || '')}" direction="${xmlEscape(item.direction || 'start')}"/>`); }); xmlParts.push(" </swapWords>"); } if (Object.keys(_editorStats).length > 0) { xmlParts.push(" <statistics>"); Object.entries(_editorStats).forEach(([userId, data]) => { xmlParts.push(` <editor id="${xmlEscape(userId)}" name="${xmlEscape(data?.userName || '')}" total_count="${data?.total_count || 0}" monthly_count="${data?.monthly_count || 0}" monthly_period="${xmlEscape(data?.monthly_period || '')}" weekly_count="${data?.weekly_count || 0}" weekly_period="${xmlEscape(data?.weekly_period || '')}" daily_count="${data?.daily_count || 0}" daily_period="${xmlEscape(data?.daily_period || '')}" last_update="${data?.last_update || 0}" />`); }); xmlParts.push(" </statistics>"); } if (_excludedPlaces.size > 0) { xmlParts.push(" <excludedPlaces>"); Array.from(_excludedPlaces.entries()).sort((a,b) => String(a[0]).localeCompare(String(b[0]))).forEach(([id, name]) => { xmlParts.push(` <place id="${xmlEscape(String(id))}" name="${xmlEscape(String(name || ''))}"/>`); }); xmlParts.push(" </excludedPlaces>"); } if (typeof exportProcessedPlacesSectionXML === 'function' && _processedPlaces.length > 0) { xmlParts.push(exportProcessedPlacesSectionXML()); } const xmlContent = `<?xml version="1.0" encoding="UTF-8"?>\n<${rootTagName}>\n${xmlParts.join("\n")}\n</${rootTagName}>`; const blob = new Blob([xmlContent], { type: "application/xml;charset=utf-8" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = fileName; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } catch (err) { console.error('[WME PLN] exportSharedDataToXml error:', err); alert('No fue posible exportar el XML. Revisa la consola para más detalles.'); } } function handleXmlFileDrop(file, type = "words") { if (!file || !(file.type === "text/xml" || (file.name || '').endsWith(".xml"))) { alert("Por favor, arrastra un archivo XML válido."); return; } const reader = new FileReader(); reader.onload = function(evt) { try { let newExcludedAdded = 0, newReplacementsAdded = 0, replacementsOverwritten = 0, newSwapWordsAdded = 0, newPlacesAdded = 0; const parser = new DOMParser(); const xmlDoc = parser.parseFromString(evt.target.result, "application/xml"); const parserError = xmlDoc.querySelector("parsererror"); if (parserError) { alert("Error al parsear el archivo XML: " + parserError.textContent); return; } const rootTag = (xmlDoc.documentElement.tagName || '').toLowerCase(); if (type === "words") { if (rootTag !== "excludedwords") { alert("El XML no es válido para Palabras Especiales. Raíz esperada: <ExcludedWords>."); return; } const wordsNode = xmlDoc.querySelector("excludedwords > words"); if (wordsNode) { const items = Array.from(wordsNode.querySelectorAll("word")).map(n => (n.textContent || '').trim()).filter(Boolean); if (!window.excludedWords || !(window.excludedWords instanceof Set)) window.excludedWords = new Set(); items.forEach(w => { if (!window.excludedWords.has(w)) { window.excludedWords.add(w); newExcludedAdded++; } }); } const replNode = xmlDoc.querySelector("excludedwords > replacements"); if (replNode) { if (!window.replacementWords || typeof window.replacementWords !== 'object') window.replacementWords = {}; Array.from(replNode.querySelectorAll("replacement")).forEach(n => { const from = n.getAttribute("from") || ""; const to = (n.textContent || "").trim(); if (!from) return; if (from in window.replacementWords) replacementsOverwritten++; else newReplacementsAdded++; window.replacementWords[from] = to; }); } const swapNode = xmlDoc.querySelector("excludedwords > swapwords"); if (swapNode) { if (!Array.isArray(window.swapWords)) window.swapWords = []; Array.from(swapNode.querySelectorAll("swap")).forEach(n => { const word = n.getAttribute("value") || ""; const direction = n.getAttribute("direction") || "start"; if (word) { window.swapWords.push({ word, direction }); newSwapWordsAdded++; } }); } const statsNode = xmlDoc.querySelector("excludedwords > statistics"); if (statsNode && window.editorStats && typeof window.editorStats === 'object') { const editorNode = statsNode.querySelector("editor"); if (editorNode) { const editorId = editorNode.getAttribute("id"); if (editorId && window.editorStats[editorId]) { window.editorStats[editorId] = { userName: editorNode.getAttribute("name") || window.editorStats[editorId].userName, total_count: parseInt(editorNode.getAttribute("total_count"), 10) || 0, monthly_count: parseInt(editorNode.getAttribute("monthly_count"), 10) || 0, monthly_period: editorNode.getAttribute("monthly_period") || '', weekly_count: parseInt(editorNode.getAttribute("weekly_count"), 10) || 0, weekly_period: editorNode.getAttribute("weekly_period") || '', daily_count: parseInt(editorNode.getAttribute("daily_count"), 10) || 0, daily_period: editorNode.getAttribute("daily_period") || '', last_update: parseInt(editorNode.getAttribute("last_update"), 10) || 0 }; } } } try { saveExcludedWordsToLocalStorage?.(); } catch (_) { } try { saveReplacementWordsToStorage?.(); } catch (_) { } try { saveSwapWordsToStorage?.(); } catch (_) { } try { saveEditorStats?.(); } catch (_) { } try { renderExcludedWordsList?.(document.getElementById("excludedWordsList")); } catch (_) { } try { renderReplacementsList?.(document.getElementById("replacementsListElementID")); } catch (_) { } try { updateStatsDisplay?.(); } catch (_) { } alert(`Importación completada.\n- Palabras nuevas: ${newExcludedAdded}\n- Reemplazos nuevos: ${newReplacementsAdded}\n- Reemplazos sobrescritos: ${replacementsOverwritten}\n- SwapWords importadas: ${newSwapWordsAdded}`); } else if (type === "places") { if (rootTag !== "excludedplaces") { alert("El XML no es válido para Lugares Excluidos. Raíz esperada: <ExcludedPlaces>."); return; } if (!(window.excludedPlaces instanceof Map)) window.excludedPlaces = new Map(); const nodes = xmlDoc.querySelectorAll("excludedplaces > placeids > placeid"); nodes.forEach(n => { const id = n.getAttribute("id") || ""; const name = n.getAttribute("name") || ""; if (id && !window.excludedPlaces.has(id)) { window.excludedPlaces.set(id, name); newPlacesAdded++; } }); try { saveExcludedPlacesToLocalStorage?.(); } catch (_) { } try { renderExcludedPlacesList?.(document.getElementById("excludedPlacesListUL")); } catch (_) { } alert(`Importación completada.\n- Lugares excluidos nuevos: ${newPlacesAdded}`); } } catch (err) { console.error("[WME PLN] Error procesando el XML:", err); alert("Ocurrió un error procesando el archivo XML."); } }; reader.readAsText(file); } function exportProcessedPlacesSectionXML() { try { if (!Array.isArray(window.processedPlaces) || window.processedPlaces.length === 0) { return ''; } let xml = ' <processedPlaces>\n'; window.processedPlaces.forEach(p => { xml += ` <place id="${xmlEscape(p.placeId || '')}" name="${xmlEscape(p.name || '')}" city="${xmlEscape(p.city || '')}" department="${xmlEscape(p.department || '')}" modifiedAt="${xmlEscape(p.modifiedAt || '')}" editorId="${xmlEscape(p.editorId || '')}" editorName="${xmlEscape(p.editorName || '')}" />\n`; }); xml += ' </processedPlaces>'; return xml; } catch (e) { console.error('[WME PLN] Error generando XML de lugares procesados:', e); return ''; } } function importProcessedPlacesFromXML(xmlString) { try { if (!xmlString || typeof xmlString !== 'string') return; const parser = new DOMParser(); const xmlDoc = parser.parseFromString(xmlString, "application/xml"); const placeNodes = xmlDoc.querySelectorAll("processedPlaces > place"); if (placeNodes.length === 0) return; let importedCount = 0; placeNodes.forEach(node => { const entry = { placeId: node.getAttribute('id') || '', name: node.getAttribute('name') || '', city: node.getAttribute('city') || 'N/A', department: node.getAttribute('department') || 'N/A', modifiedAt: node.getAttribute('modifiedAt') || new Date().toISOString(), editorId: Number(node.getAttribute('editorId')) || 0, editorName: node.getAttribute('editorName') || 'Importado' }; if (entry.placeId && entry.editorId) { addProcessedPlace(entry); importedCount++; } }); if (importedCount > 0) { plnToast(`✅ Importados ${importedCount} registros de Lugares Procesados.`, 2500); } } catch (e) { console.error('[WME PLN] Error importando XML de lugares procesados:', e); } } function plnAttachProcessedPlacesToXML(xmlString) { try { let s = String(xmlString || ''); if (!s.trim()) return s; if (!s.match(/<\w+.*?>/)) s = `<data>${s}</data>`; if (typeof exportProcessedPlacesSectionXML === 'function' && !/<processedPlaces[\s>]/i.test(s)) { const processedSection = '\n' + exportProcessedPlacesSectionXML() + '\n'; if (s.includes('</ExcludedWords>')) { s = s.replace(/\n?<\/ExcludedWords>\s*$/i, processedSection + '</ExcludedWords>'); } else if (s.includes('</data>')) { s = s.replace(/\n?<\/data>\s*$/i, processedSection + '</data>'); } else { s += processedSection; } } return s; } catch (_) { return xmlString; } }