MediaWiki:Common.js
Aparência da Noite
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/* ==========================================================================
VAMPIRO: A MÁSCARA — SCRIPTS GLOBAIS, SEGREDO DINÂMICO & EDITOR TOOLS
========================================================================== */
(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') {
isRevealed = true;
} else if (liberado === 'nao' || liberado === 'false' || liberado === '0') {
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. BARRA DE FERRAMENTAS NO EDITOR DE CÓDIGO-FONTE
// ------------------------------------------------------------------------
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.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;';
// Label
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);
// Btn: Inserir Censura Inline
bar.appendChild(makeBtn(
'Censurar Trecho',
'Envolve o texto selecionado com {{Segredo|...|liberado=nao}}',
'#240c12', '#ff2a3f',
function () { insertSecret(textbox, false); }
));
// Btn: Inserir Bloco
bar.appendChild(makeBtn(
'Dossie Bloco',
'Insere bloco confidencial completo',
'#181822', '#3d0a12',
function () { insertSecret(textbox, true); }
));
// Btn: Remover Censura
bar.appendChild(makeBtn(
'Remover Censura',
'Remove a marcacao {{Segredo|...}} do trecho selecionado, mantendo o texto interno',
'#0c1a12', '#1a6b3a',
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.8'; };
btn.onmouseout = function () { btn.style.opacity = '1'; };
btn.onclick = function (e) { e.preventDefault(); onclick(); };
return btn;
}
function insertSecret(textbox, isBlock) {
var start = textbox.selectionStart;
var end = textbox.selectionEnd;
var text = textbox.value;
var selected = text.substring(start, end);
var pre, post, defaultText;
if (isBlock) {
pre = '\n{{Segredo|tipo=bloco|nivel=CONFIDENCIAL|data=AAAA-MM-DD|dica=Descricao|\n';
post = '\n}}\n';
defaultText = selected || 'Texto do relatorio confidencial...';
} else {
pre = '{{Segredo|';
post = '|data=AAAA-MM-DD|liberado=nao}}';
defaultText = selected || 'Texto censurado';
}
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;
// Expand selection to find the surrounding {{Segredo|...}}
// Search backwards from cursor for {{Segredo|
var searchStart = text.lastIndexOf('{{Segredo|', start);
if (searchStart === -1) {
alert('Nenhum {{Segredo|...}} encontrado na posicao do cursor.\nPosicione o cursor dentro de um trecho censurado.');
return;
}
// Find the matching closing }}
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('Nao foi possivel encontrar o fechamento }} correspondente.');
return;
}
var fullMatch = text.substring(searchStart, searchEnd);
// Extract the content (first unnamed parameter)
// Pattern: {{Segredo|CONTENT|param=val...}} or {{Segredo|param=val|CONTENT}}
var inner = fullMatch.substring('{{Segredo|'.length, fullMatch.length - 2);
// Split by | but respect nested {{ }}
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);
// Find the unnamed parameter (content) — the one without '='
var contentText = '';
for (var k = 0; k < params.length; k++) {
var p = params[k].trim();
if (p.indexOf('=') === -1) {
contentText = p;
break;
}
}
if (!contentText) {
// Try finding param starting with content that has multiline
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'); }
}
// ------------------------------------------------------------------------
// 3. 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();
}
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();
});
}
})();