MediaWiki:Common.js: Difference between revisions
Aparência da Noite
Adicionado botao Remover Censura na barra de ferramentas |
Substituído campo de texto por botões visuais [CENSURADO | LIBERADO] no modal e toolbar |
||
| Line 1: | Line 1: | ||
/* ========================================================================== | /* ========================================================================== | ||
VAMPIRO: A MÁSCARA — SCRIPTS GLOBAIS, SEGREDO DINÂMICO & | VAMPIRO: A MÁSCARA — SCRIPTS GLOBAIS, SEGREDO DINÂMICO & INTERFACE DE BOTÕES | ||
========================================================================== */ | ========================================================================== */ | ||
| Line 22: | Line 22: | ||
var tooltipText = '[ARQUIVO CONFIDENCIAL]'; | var tooltipText = '[ARQUIVO CONFIDENCIAL]'; | ||
if (liberado === 'sim' || liberado === 'true' || liberado === '1') { | if (liberado === 'sim' || liberado === 'true' || liberado === '1' || liberado === 'liberado') { | ||
isRevealed = true; | isRevealed = true; | ||
} else if (liberado === 'nao' || liberado === 'false' || liberado === '0') { | } else if (liberado === 'nao' || liberado === 'false' || liberado === '0' || liberado === 'censurado') { | ||
isRevealed = false; | isRevealed = false; | ||
tooltipText = '[CLASSIFICADO' + (dica ? ' // ' + dica : '') + ']'; | tooltipText = '[CLASSIFICADO' + (dica ? ' // ' + dica : '') + ']'; | ||
| Line 63: | Line 63: | ||
// ------------------------------------------------------------------------ | // ------------------------------------------------------------------------ | ||
// 2. BARRA DE FERRAMENTAS NO EDITOR DE CÓDIGO-FONTE | // 2. ENHANCER PARA O MODAL DO VISUALEDITOR: BOTÕES [ CENSURADO | LIBERADO ] | ||
// ------------------------------------------------------------------------ | |||
function enhanceVisualEditorModal() { | |||
var dialogs = document.querySelectorAll('.ve-ui-mwTransclusionDialog'); | |||
if (!dialogs.length) return; | |||
dialogs.forEach(function (dialog) { | |||
// Procura campos de parâmetros do template | |||
var fields = dialog.querySelectorAll('.oo-ui-fieldLayout'); | |||
fields.forEach(function (field) { | |||
var labelEl = field.querySelector('.oo-ui-labelElement-label'); | |||
if (!labelEl) return; | |||
var labelText = labelEl.textContent.trim().toLowerCase(); | |||
// Se o campo for o parâmetro 'liberado' | |||
if (labelText === 'liberado' || labelText.indexOf('liberado') !== -1) { | |||
var inputWrapper = field.querySelector('.oo-ui-textInputWidget'); | |||
var input = field.querySelector('input, textarea'); | |||
if (!input || field.dataset.vtmEnhanced === 'true') return; | |||
field.dataset.vtmEnhanced = 'true'; | |||
// Cria container dos botões | |||
var btnGroup = document.createElement('div'); | |||
btnGroup.className = 'vtm-status-toggle-group'; | |||
btnGroup.style.cssText = 'display:flex; gap:10px; margin:8px 0 12px 0; align-items:center;'; | |||
// Botão 1: CENSURADO | |||
var btnCensurado = document.createElement('button'); | |||
btnCensurado.type = 'button'; | |||
btnCensurado.className = 'vtm-toggle-btn vtm-btn-censurado'; | |||
btnCensurado.innerHTML = '⬛ CENSURADO'; | |||
btnCensurado.style.cssText = 'flex:1; padding:8px 14px; border-radius:4px; font-weight:700; font-size:0.88em; cursor:pointer; font-family:"Cinzel", Georgia, serif; letter-spacing:1px; transition:all 0.2s ease;'; | |||
// Botão 2: LIBERADO | |||
var btnLiberado = document.createElement('button'); | |||
btnLiberado.type = 'button'; | |||
btnLiberado.className = 'vtm-toggle-btn vtm-btn-liberado'; | |||
btnLiberado.innerHTML = '🔓 LIBERADO'; | |||
btnLiberado.style.cssText = 'flex:1; padding:8px 14px; border-radius:4px; font-weight:700; font-size:0.88em; cursor:pointer; font-family:"Cinzel", Georgia, serif; letter-spacing:1px; transition:all 0.2s ease;'; | |||
function updateStyles(val) { | |||
val = (val || '').toLowerCase().trim(); | |||
if (val === 'sim' || val === '1' || val === 'true' || val === 'liberado') { | |||
// Liberado ativo | |||
btnLiberado.style.background = 'linear-gradient(180deg, #1b5e20 0%, #0d3311 100%)'; | |||
btnLiberado.style.color = '#ffffff'; | |||
btnLiberado.style.border = '2px solid #4caf50'; | |||
btnLiberado.style.boxShadow = '0 0 10px rgba(76, 175, 80, 0.6)'; | |||
btnCensurado.style.background = '#141418'; | |||
btnCensurado.style.color = '#888892'; | |||
btnCensurado.style.border = '1px solid #333340'; | |||
btnCensurado.style.boxShadow = 'none'; | |||
} else { | |||
// Censurado ativo (padrão) | |||
btnCensurado.style.background = 'linear-gradient(180deg, #5c0f18 0%, #2b060a 100%)'; | |||
btnCensurado.style.color = '#ffffff'; | |||
btnCensurado.style.border = '2px solid #ff2a3f'; | |||
btnCensurado.style.boxShadow = '0 0 10px rgba(255, 42, 63, 0.6)'; | |||
btnLiberado.style.background = '#141418'; | |||
btnLiberado.style.color = '#888892'; | |||
btnLiberado.style.border = '1px solid #333340'; | |||
btnLiberado.style.boxShadow = 'none'; | |||
} | |||
} | |||
// Ações de clique | |||
btnCensurado.onclick = function (e) { | |||
e.preventDefault(); | |||
input.value = 'nao'; | |||
if (window.$) { $(input).trigger('input').trigger('change'); } | |||
updateStyles('nao'); | |||
}; | |||
btnLiberado.onclick = function (e) { | |||
e.preventDefault(); | |||
input.value = 'sim'; | |||
if (window.$) { $(input).trigger('input').trigger('change'); } | |||
updateStyles('sim'); | |||
}; | |||
// Monitora alterações manuais | |||
input.addEventListener('input', function () { | |||
updateStyles(input.value); | |||
}); | |||
// Estado inicial | |||
updateStyles(input.value || 'nao'); | |||
// Monta na tela | |||
btnGroup.appendChild(btnCensurado); | |||
btnGroup.appendChild(btnLiberado); | |||
if (inputWrapper) { | |||
inputWrapper.parentNode.insertBefore(btnGroup, inputWrapper); | |||
// Oculta o input textual bruto para ficar 100% visual | |||
inputWrapper.style.display = 'none'; | |||
} | |||
} | |||
}); | |||
}); | |||
} | |||
// Observador contínuo para o modal do VisualEditor | |||
var modalObserver = new MutationObserver(function () { | |||
enhanceVisualEditorModal(); | |||
}); | |||
modalObserver.observe(document.body, { childList: true, subtree: true }); | |||
// ------------------------------------------------------------------------ | |||
// 3. BARRA DE FERRAMENTAS NO EDITOR DE CÓDIGO-FONTE (#wpTextbox1) | |||
// ------------------------------------------------------------------------ | // ------------------------------------------------------------------------ | ||
function initVtmQuickToolbar() { | function initVtmQuickToolbar() { | ||
| Line 72: | Line 184: | ||
var bar = document.createElement('div'); | var bar = document.createElement('div'); | ||
bar.id = 'vtm-editor-quickbar'; | bar.id = 'vtm-editor-quickbar'; | ||
bar.className = 'vtm-editor-quickbar'; | |||
bar.style.cssText = 'display:flex; flex-wrap:wrap; gap:8px; align-items:center; padding:10px 14px; margin:10px 0; background:linear-gradient(90deg, #181014 0%, #0d0d12 100%); border:1px solid #3d0a12; border-left:4px solid #8b0000; border-radius:4px; font-family:"Cinzel", Georgia, serif; box-shadow:0 4px 14px rgba(0,0,0,0.6); z-index:99;'; | bar.style.cssText = 'display:flex; flex-wrap:wrap; gap:8px; align-items:center; padding:10px 14px; margin:10px 0; background:linear-gradient(90deg, #181014 0%, #0d0d12 100%); border:1px solid #3d0a12; border-left:4px solid #8b0000; border-radius:4px; font-family:"Cinzel", Georgia, serif; box-shadow:0 4px 14px rgba(0,0,0,0.6); z-index:99;'; | ||
var label = document.createElement('span'); | var label = document.createElement('span'); | ||
label.style.cssText = 'color:#c5a059; font-size:0.85em; font-weight:700; text-transform:uppercase; letter-spacing:1px; margin-right:6px;'; | label.style.cssText = 'color:#c5a059; font-size:0.85em; font-weight:700; text-transform:uppercase; letter-spacing:1px; margin-right:6px;'; | ||
label.textContent = 'Ferramentas:'; | label.textContent = '🩸 Ferramentas:'; | ||
bar.appendChild(label); | bar.appendChild(label); | ||
// | // 1. Inserir Censurado | ||
bar.appendChild(makeBtn( | bar.appendChild(makeBtn( | ||
'Censurar Trecho', | '⬛ Censurar Trecho', | ||
'Envolve o texto | 'Envolve o texto com {{Segredo|...|liberado=nao}}', | ||
'#240c12', '#ff2a3f', | '#240c12', '#ff2a3f', | ||
function () { | function () { insertSecretWithStatus(textbox, 'nao'); } | ||
)); | |||
// 2. Inserir Liberado | |||
bar.appendChild(makeBtn( | |||
'🔓 Inserir Liberado', | |||
'Envolve o texto com {{Segredo|...|liberado=sim}}', | |||
'#0d2915', '#2e7d32', | |||
function () { insertSecretWithStatus(textbox, 'sim'); } | |||
)); | )); | ||
// | // 3. Dossiê Bloco | ||
bar.appendChild(makeBtn( | bar.appendChild(makeBtn( | ||
' | '📁 Dossiê Bloco', | ||
'Insere bloco confidencial completo', | 'Insere bloco confidencial completo', | ||
'#181822', '#3d0a12', | '#181822', '#3d0a12', | ||
function () { | function () { insertSecretBlock(textbox); } | ||
)); | )); | ||
// | // 4. Remover Censura | ||
bar.appendChild(makeBtn( | bar.appendChild(makeBtn( | ||
'Remover Censura', | '🗑️ Remover Censura', | ||
'Remove a | 'Remove a marcação {{Segredo|...}} mantendo o texto interno', | ||
'# | '#1a1a22', '#4a4a58', | ||
function () { removeSecret(textbox); } | function () { removeSecret(textbox); } | ||
)); | )); | ||
| Line 113: | Line 233: | ||
btn.textContent = text; | btn.textContent = text; | ||
btn.title = title; | btn.title = title; | ||
btn.onmouseover = function () { btn.style.opacity = '0. | btn.onmouseover = function () { btn.style.opacity = '0.85'; }; | ||
btn.onmouseout = function () { btn.style.opacity = '1'; }; | btn.onmouseout = function () { btn.style.opacity = '1'; }; | ||
btn.onclick = function (e) { e.preventDefault(); onclick(); }; | btn.onclick = function (e) { e.preventDefault(); onclick(); }; | ||
| Line 119: | Line 239: | ||
} | } | ||
function | function insertSecretWithStatus(textbox, status) { | ||
var start = textbox.selectionStart; | var start = textbox.selectionStart; | ||
var end = textbox.selectionEnd; | var end = textbox.selectionEnd; | ||
| Line 125: | Line 245: | ||
var selected = text.substring(start, end); | var selected = text.substring(start, end); | ||
var pre, | var pre = '{{Segredo|'; | ||
if ( | var defaultText = selected || 'Texto sensível'; | ||
var post = '|liberado=' + status + '}}'; | |||
var replacement = pre + defaultText + post; | |||
textbox.value = text.substring(0, start) + replacement + text.substring(end); | |||
var cursorStart = start + pre.length; | |||
textbox.focus(); | |||
textbox.setSelectionRange(cursorStart, cursorStart + defaultText.length); | |||
if (window.$) { $(textbox).trigger('input').trigger('change'); } | |||
} | |||
function insertSecretBlock(textbox) { | |||
var start = textbox.selectionStart; | |||
var end = textbox.selectionEnd; | |||
var text = textbox.value; | |||
var selected = text.substring(start, end); | |||
var pre = '\n{{Segredo|tipo=bloco|nivel=CONFIDENCIAL // SEGUNDA INQUISIÇÃO|data=AAAA-MM-DD|dica=Operação Lisboa|\n'; | |||
var defaultText = selected || 'Texto do relatório confidencial...'; | |||
var post = '\n}}\n'; | |||
var replacement = pre + defaultText + post; | var replacement = pre + defaultText + post; | ||
| Line 151: | Line 284: | ||
var text = textbox.value; | var text = textbox.value; | ||
var searchStart = text.lastIndexOf('{{Segredo|', start); | var searchStart = text.lastIndexOf('{{Segredo|', start); | ||
if (searchStart === -1) { | if (searchStart === -1) { | ||
alert('Nenhum {{Segredo|...}} encontrado na | alert('Nenhum {{Segredo|...}} encontrado na posição do cursor.\nPosicione o cursor dentro de um trecho censurado.'); | ||
return; | return; | ||
} | } | ||
var depth = 0; | var depth = 0; | ||
var searchEnd = -1; | var searchEnd = -1; | ||
| Line 172: | Line 302: | ||
if (searchEnd === -1) { | if (searchEnd === -1) { | ||
alert(' | alert('Não foi possível encontrar o fechamento }} correspondente.'); | ||
return; | return; | ||
} | } | ||
var fullMatch = text.substring(searchStart, searchEnd); | var fullMatch = text.substring(searchStart, searchEnd); | ||
var inner = fullMatch.substring('{{Segredo|'.length, fullMatch.length - 2); | var inner = fullMatch.substring('{{Segredo|'.length, fullMatch.length - 2); | ||
var params = []; | var params = []; | ||
var current = ''; | var current = ''; | ||
| Line 194: | Line 320: | ||
params.push(current); | params.push(current); | ||
var contentText = ''; | var contentText = ''; | ||
for (var k = 0; k < params.length; k++) { | for (var k = 0; k < params.length; k++) { | ||
| Line 205: | Line 330: | ||
if (!contentText) { | if (!contentText) { | ||
for (var m = 0; m < params.length; m++) { | for (var m = 0; m < params.length; m++) { | ||
if (params[m].indexOf('=') === -1) { | if (params[m].indexOf('=') === -1) { | ||
| Line 215: | Line 339: | ||
contentText = contentText.trim(); | contentText = contentText.trim(); | ||
textbox.value = text.substring(0, searchStart) + contentText + text.substring(searchEnd); | textbox.value = text.substring(0, searchStart) + contentText + text.substring(searchEnd); | ||
textbox.focus(); | textbox.focus(); | ||
| Line 224: | Line 347: | ||
// ------------------------------------------------------------------------ | // ------------------------------------------------------------------------ | ||
// | // 4. ANIMAÇÃO DE CASCATA DAS SEÇÕES | ||
// ------------------------------------------------------------------------ | // ------------------------------------------------------------------------ | ||
function initSectionAnimation() { | function initSectionAnimation() { | ||
| Line 274: | Line 397: | ||
initSectionAnimation(); | initSectionAnimation(); | ||
initVtmQuickToolbar(); | initVtmQuickToolbar(); | ||
enhanceVisualEditorModal(); | |||
} | } | ||
Revision as of 19:48, 1 September 2026
/* ==========================================================================
VAMPIRO: A MÁSCARA — SCRIPTS GLOBAIS, SEGREDO DINÂMICO & INTERFACE DE BOTÕES
========================================================================== */
(function () {
'use strict';
// ------------------------------------------------------------------------
// 1. LÓGICA DE CENSURA & REVELAÇÃO DINÂMICA ({{Segredo}})
// ------------------------------------------------------------------------
function initVtmSecrets() {
var secretElements = document.querySelectorAll('[data-vtm-secret="true"]');
if (!secretElements || secretElements.length === 0) return;
var now = new Date();
secretElements.forEach(function (el) {
var liberado = (el.getAttribute('data-liberado') || '').toLowerCase().trim();
var dateStr = (el.getAttribute('data-reveal-date') || '').trim();
var dica = (el.getAttribute('data-dica') || '').trim();
var isRevealed = false;
var tooltipText = '[ARQUIVO CONFIDENCIAL]';
if (liberado === 'sim' || liberado === 'true' || liberado === '1' || liberado === 'liberado') {
isRevealed = true;
} else if (liberado === 'nao' || liberado === 'false' || liberado === '0' || liberado === 'censurado') {
isRevealed = false;
tooltipText = '[CLASSIFICADO' + (dica ? ' // ' + dica : '') + ']';
} else if (dateStr) {
var targetDate = null;
if (dateStr.indexOf('/') !== -1) {
var parts = dateStr.split('/');
if (parts.length === 3) {
targetDate = new Date(parts[2], parseInt(parts[1], 10) - 1, parts[0], 0, 0, 0);
}
} else {
targetDate = new Date(dateStr);
}
if (targetDate && !isNaN(targetDate.getTime())) {
if (now >= targetDate) {
isRevealed = true;
} else {
var formatted = ('0' + targetDate.getDate()).slice(-2) + '/' +
('0' + (targetDate.getMonth() + 1)).slice(-2) + '/' +
targetDate.getFullYear();
tooltipText = '[CLASSIFICADO' + (dica ? ' // ' + dica : '') + ' - ' + formatted + ']';
}
}
}
if (isRevealed) {
el.classList.add('is-revealed');
el.classList.remove('is-redacted');
el.title = '[DESCLASSIFICADO' + (dica ? ' // ' + dica : '') + ']';
} else {
el.classList.add('is-redacted');
el.classList.remove('is-revealed');
el.title = tooltipText;
}
});
}
// ------------------------------------------------------------------------
// 2. ENHANCER PARA O MODAL DO VISUALEDITOR: BOTÕES [ CENSURADO | LIBERADO ]
// ------------------------------------------------------------------------
function enhanceVisualEditorModal() {
var dialogs = document.querySelectorAll('.ve-ui-mwTransclusionDialog');
if (!dialogs.length) return;
dialogs.forEach(function (dialog) {
// Procura campos de parâmetros do template
var fields = dialog.querySelectorAll('.oo-ui-fieldLayout');
fields.forEach(function (field) {
var labelEl = field.querySelector('.oo-ui-labelElement-label');
if (!labelEl) return;
var labelText = labelEl.textContent.trim().toLowerCase();
// Se o campo for o parâmetro 'liberado'
if (labelText === 'liberado' || labelText.indexOf('liberado') !== -1) {
var inputWrapper = field.querySelector('.oo-ui-textInputWidget');
var input = field.querySelector('input, textarea');
if (!input || field.dataset.vtmEnhanced === 'true') return;
field.dataset.vtmEnhanced = 'true';
// Cria container dos botões
var btnGroup = document.createElement('div');
btnGroup.className = 'vtm-status-toggle-group';
btnGroup.style.cssText = 'display:flex; gap:10px; margin:8px 0 12px 0; align-items:center;';
// Botão 1: CENSURADO
var btnCensurado = document.createElement('button');
btnCensurado.type = 'button';
btnCensurado.className = 'vtm-toggle-btn vtm-btn-censurado';
btnCensurado.innerHTML = '⬛ CENSURADO';
btnCensurado.style.cssText = 'flex:1; padding:8px 14px; border-radius:4px; font-weight:700; font-size:0.88em; cursor:pointer; font-family:"Cinzel", Georgia, serif; letter-spacing:1px; transition:all 0.2s ease;';
// Botão 2: LIBERADO
var btnLiberado = document.createElement('button');
btnLiberado.type = 'button';
btnLiberado.className = 'vtm-toggle-btn vtm-btn-liberado';
btnLiberado.innerHTML = '🔓 LIBERADO';
btnLiberado.style.cssText = 'flex:1; padding:8px 14px; border-radius:4px; font-weight:700; font-size:0.88em; cursor:pointer; font-family:"Cinzel", Georgia, serif; letter-spacing:1px; transition:all 0.2s ease;';
function updateStyles(val) {
val = (val || '').toLowerCase().trim();
if (val === 'sim' || val === '1' || val === 'true' || val === 'liberado') {
// Liberado ativo
btnLiberado.style.background = 'linear-gradient(180deg, #1b5e20 0%, #0d3311 100%)';
btnLiberado.style.color = '#ffffff';
btnLiberado.style.border = '2px solid #4caf50';
btnLiberado.style.boxShadow = '0 0 10px rgba(76, 175, 80, 0.6)';
btnCensurado.style.background = '#141418';
btnCensurado.style.color = '#888892';
btnCensurado.style.border = '1px solid #333340';
btnCensurado.style.boxShadow = 'none';
} else {
// Censurado ativo (padrão)
btnCensurado.style.background = 'linear-gradient(180deg, #5c0f18 0%, #2b060a 100%)';
btnCensurado.style.color = '#ffffff';
btnCensurado.style.border = '2px solid #ff2a3f';
btnCensurado.style.boxShadow = '0 0 10px rgba(255, 42, 63, 0.6)';
btnLiberado.style.background = '#141418';
btnLiberado.style.color = '#888892';
btnLiberado.style.border = '1px solid #333340';
btnLiberado.style.boxShadow = 'none';
}
}
// Ações de clique
btnCensurado.onclick = function (e) {
e.preventDefault();
input.value = 'nao';
if (window.$) { $(input).trigger('input').trigger('change'); }
updateStyles('nao');
};
btnLiberado.onclick = function (e) {
e.preventDefault();
input.value = 'sim';
if (window.$) { $(input).trigger('input').trigger('change'); }
updateStyles('sim');
};
// Monitora alterações manuais
input.addEventListener('input', function () {
updateStyles(input.value);
});
// Estado inicial
updateStyles(input.value || 'nao');
// Monta na tela
btnGroup.appendChild(btnCensurado);
btnGroup.appendChild(btnLiberado);
if (inputWrapper) {
inputWrapper.parentNode.insertBefore(btnGroup, inputWrapper);
// Oculta o input textual bruto para ficar 100% visual
inputWrapper.style.display = 'none';
}
}
});
});
}
// Observador contínuo para o modal do VisualEditor
var modalObserver = new MutationObserver(function () {
enhanceVisualEditorModal();
});
modalObserver.observe(document.body, { childList: true, subtree: true });
// ------------------------------------------------------------------------
// 3. BARRA DE FERRAMENTAS NO EDITOR DE CÓDIGO-FONTE (#wpTextbox1)
// ------------------------------------------------------------------------
function initVtmQuickToolbar() {
var textbox = document.getElementById('wpTextbox1');
if (!textbox) return;
if (document.getElementById('vtm-editor-quickbar')) return;
var bar = document.createElement('div');
bar.id = 'vtm-editor-quickbar';
bar.className = 'vtm-editor-quickbar';
bar.style.cssText = 'display:flex; flex-wrap:wrap; gap:8px; align-items:center; padding:10px 14px; margin:10px 0; background:linear-gradient(90deg, #181014 0%, #0d0d12 100%); border:1px solid #3d0a12; border-left:4px solid #8b0000; border-radius:4px; font-family:"Cinzel", Georgia, serif; box-shadow:0 4px 14px rgba(0,0,0,0.6); z-index:99;';
var label = document.createElement('span');
label.style.cssText = 'color:#c5a059; font-size:0.85em; font-weight:700; text-transform:uppercase; letter-spacing:1px; margin-right:6px;';
label.textContent = '🩸 Ferramentas:';
bar.appendChild(label);
// 1. Inserir Censurado
bar.appendChild(makeBtn(
'⬛ Censurar Trecho',
'Envolve o texto com {{Segredo|...|liberado=nao}}',
'#240c12', '#ff2a3f',
function () { insertSecretWithStatus(textbox, 'nao'); }
));
// 2. Inserir Liberado
bar.appendChild(makeBtn(
'🔓 Inserir Liberado',
'Envolve o texto com {{Segredo|...|liberado=sim}}',
'#0d2915', '#2e7d32',
function () { insertSecretWithStatus(textbox, 'sim'); }
));
// 3. Dossiê Bloco
bar.appendChild(makeBtn(
'📁 Dossiê Bloco',
'Insere bloco confidencial completo',
'#181822', '#3d0a12',
function () { insertSecretBlock(textbox); }
));
// 4. Remover Censura
bar.appendChild(makeBtn(
'🗑️ Remover Censura',
'Remove a marcação {{Segredo|...}} mantendo o texto interno',
'#1a1a22', '#4a4a58',
function () { removeSecret(textbox); }
));
textbox.parentNode.insertBefore(bar, textbox);
}
function makeBtn(text, title, bg, border, onclick) {
var btn = document.createElement('button');
btn.type = 'button';
btn.style.cssText = 'background:' + bg + '; color:#ffffff; border:1px solid ' + border + '; border-radius:3px; padding:6px 12px; font-size:0.82em; font-weight:700; cursor:pointer; display:inline-flex; align-items:center; gap:4px; transition:all 0.2s ease;';
btn.textContent = text;
btn.title = title;
btn.onmouseover = function () { btn.style.opacity = '0.85'; };
btn.onmouseout = function () { btn.style.opacity = '1'; };
btn.onclick = function (e) { e.preventDefault(); onclick(); };
return btn;
}
function insertSecretWithStatus(textbox, status) {
var start = textbox.selectionStart;
var end = textbox.selectionEnd;
var text = textbox.value;
var selected = text.substring(start, end);
var pre = '{{Segredo|';
var defaultText = selected || 'Texto sensível';
var post = '|liberado=' + status + '}}';
var replacement = pre + defaultText + post;
textbox.value = text.substring(0, start) + replacement + text.substring(end);
var cursorStart = start + pre.length;
textbox.focus();
textbox.setSelectionRange(cursorStart, cursorStart + defaultText.length);
if (window.$) { $(textbox).trigger('input').trigger('change'); }
}
function insertSecretBlock(textbox) {
var start = textbox.selectionStart;
var end = textbox.selectionEnd;
var text = textbox.value;
var selected = text.substring(start, end);
var pre = '\n{{Segredo|tipo=bloco|nivel=CONFIDENCIAL // SEGUNDA INQUISIÇÃO|data=AAAA-MM-DD|dica=Operação Lisboa|\n';
var defaultText = selected || 'Texto do relatório confidencial...';
var post = '\n}}\n';
var replacement = pre + defaultText + post;
textbox.value = text.substring(0, start) + replacement + text.substring(end);
var cursorStart = start + pre.length;
textbox.focus();
textbox.setSelectionRange(cursorStart, cursorStart + defaultText.length);
if (window.$) { $(textbox).trigger('input').trigger('change'); }
}
function removeSecret(textbox) {
var start = textbox.selectionStart;
var end = textbox.selectionEnd;
var text = textbox.value;
var searchStart = text.lastIndexOf('{{Segredo|', start);
if (searchStart === -1) {
alert('Nenhum {{Segredo|...}} encontrado na posição do cursor.\nPosicione o cursor dentro de um trecho censurado.');
return;
}
var depth = 0;
var searchEnd = -1;
for (var i = searchStart; i < text.length - 1; i++) {
if (text[i] === '{' && text[i + 1] === '{') { depth++; i++; }
else if (text[i] === '}' && text[i + 1] === '}') {
depth--;
if (depth === 0) { searchEnd = i + 2; break; }
i++;
}
}
if (searchEnd === -1) {
alert('Não foi possível encontrar o fechamento }} correspondente.');
return;
}
var fullMatch = text.substring(searchStart, searchEnd);
var inner = fullMatch.substring('{{Segredo|'.length, fullMatch.length - 2);
var params = [];
var current = '';
var nestedDepth = 0;
for (var j = 0; j < inner.length; j++) {
if (inner[j] === '{' && j + 1 < inner.length && inner[j + 1] === '{') { nestedDepth++; current += '{'; j++; current += '{'; }
else if (inner[j] === '}' && j + 1 < inner.length && inner[j + 1] === '}') { nestedDepth--; current += '}'; j++; current += '}'; }
else if (inner[j] === '|' && nestedDepth === 0) { params.push(current); current = ''; }
else { current += inner[j]; }
}
params.push(current);
var contentText = '';
for (var k = 0; k < params.length; k++) {
var p = params[k].trim();
if (p.indexOf('=') === -1) {
contentText = p;
break;
}
}
if (!contentText) {
for (var m = 0; m < params.length; m++) {
if (params[m].indexOf('=') === -1) {
contentText = params[m];
break;
}
}
}
contentText = contentText.trim();
textbox.value = text.substring(0, searchStart) + contentText + text.substring(searchEnd);
textbox.focus();
textbox.setSelectionRange(searchStart, searchStart + contentText.length);
if (window.$) { $(textbox).trigger('input').trigger('change'); }
}
// ------------------------------------------------------------------------
// 4. ANIMAÇÃO DE CASCATA DAS SEÇÕES
// ------------------------------------------------------------------------
function initSectionAnimation() {
var content = document.querySelector('.mw-parser-output');
if (!content || content.dataset.vtmAnimated === 'true') return;
if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
content.dataset.vtmAnimated = 'true';
var children = Array.from(content.children);
var sections = [];
var currentSection = [];
children.forEach(function (child) {
if (
child.classList.contains('catlinks') ||
child.classList.contains('mw-category-generated') ||
child.tagName === 'SCRIPT' ||
child.tagName === 'STYLE' ||
child.id === 'vtm-editor-quickbar'
) {
if (currentSection.length > 0) { sections.push(currentSection); currentSection = []; }
return;
}
var isH2 = child.tagName === 'H2' || child.classList.contains('mw-heading2');
if (isH2 && currentSection.length > 0) { sections.push(currentSection); currentSection = []; }
currentSection.push(child);
});
if (currentSection.length > 0) sections.push(currentSection);
sections.forEach(function (sectionElements, index) {
var wrapper = document.createElement('div');
wrapper.className = 'vtm-section-block';
wrapper.style.animationDelay = (0.12 + index * 0.42).toFixed(2) + 's';
var firstElem = sectionElements[0];
if (firstElem && firstElem.parentNode) {
firstElem.parentNode.insertBefore(wrapper, firstElem);
sectionElements.forEach(function (el) { wrapper.appendChild(el); });
}
});
}
// Inicialização
function initAll() {
initVtmSecrets();
initSectionAnimation();
initVtmQuickToolbar();
enhanceVisualEditorModal();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initAll);
} else {
initAll();
}
if (window.mw && window.mw.hook) {
window.mw.hook('wikipage.content').add(function () {
initVtmSecrets();
initSectionAnimation();
});
}
})();