Jump to content

MediaWiki:Common.js: Difference between revisions

From Vampiro
Substituído campo de texto por botões visuais [CENSURADO | LIBERADO] no modal e toolbar
Scanner universal para botões CENSURADO | LIBERADO nos modais OOUI
Line 1: Line 1:
/* ==========================================================================
/* ==========================================================================
   VAMPIRO: A MÁSCARA — SCRIPTS GLOBAIS, SEGREDO DINÂMICO & INTERFACE DE BOTÕES
   VAMPIRO: A MÁSCARA — SCRIPTS GLOBAIS & BOTÕES INTERATIVOS CENSURADO/LIBERADO
   ========================================================================== */
   ========================================================================== */


Line 63: Line 63:


   // ------------------------------------------------------------------------
   // ------------------------------------------------------------------------
   // 2. ENHANCER PARA O MODAL DO VISUALEDITOR: BOTÕES [ CENSURADO | LIBERADO ]
   // 2. SCANNER UNIVERSAL DO MODAL: SUBSTITUI O CAMPO TEXTO POR [CENSURADO | LIBERADO]
   // ------------------------------------------------------------------------
   // ------------------------------------------------------------------------
   function enhanceVisualEditorModal() {
   function transformLiberadoInputs() {
     var dialogs = document.querySelectorAll('.ve-ui-mwTransclusionDialog');
    // Procura todos os inputs e textareas dentro de formulários/modais OOUI
     if (!dialogs.length) return;
     var inputs = document.querySelectorAll('.oo-ui-windowManager input, .oo-ui-windowManager textarea, .ve-ui-surface input, .oo-ui-fieldLayout input, .oo-ui-fieldLayout textarea');
      
    inputs.forEach(function (input) {
      // Encontra o container pai (PageLayout ou FieldLayout)
      var container = input.closest('.oo-ui-fieldLayout') ||
                      input.closest('.ve-ui-mwParameterPage') ||
                      input.closest('.oo-ui-pageLayout') ||
                      input.parentElement;
      if (!container) return;


    dialogs.forEach(function (dialog) {
      var textCheck = (container.textContent || '').toLowerCase();
       // Procura campos de parâmetros do template
       // Verifica se o bloco se refere ao parâmetro "liberado"
       var fields = dialog.querySelectorAll('.oo-ui-fieldLayout');
       if (textCheck.indexOf('liberado') !== -1 && !container.querySelector('.vtm-custom-toggle-box')) {
      fields.forEach(function (field) {
       
         var labelEl = field.querySelector('.oo-ui-labelElement-label');
        // Cria a caixa com os 2 botões
        if (!labelEl) return;
         var box = document.createElement('div');
         var labelText = labelEl.textContent.trim().toLowerCase();
        box.className = 'vtm-custom-toggle-box';
         box.style.cssText = 'display:flex; gap:12px; margin:10px 0 16px 0; width:100%; max-width:460px;';


         // Se o campo for o parâmetro 'liberado'
         var btnCensurado = document.createElement('button');
        if (labelText === 'liberado' || labelText.indexOf('liberado') !== -1) {
        btnCensurado.type = 'button';
          var inputWrapper = field.querySelector('.oo-ui-textInputWidget');
        btnCensurado.innerHTML = '⬛ CENSURADO';
          var input = field.querySelector('input, textarea');
        btnCensurado.style.cssText = 'flex:1; padding:10px 16px; border-radius:4px; font-weight:800; font-size:0.9em; cursor:pointer; font-family:"Cinzel", Georgia, serif; letter-spacing:1px; transition:all 0.25s ease; outline:none;';
          if (!input || field.dataset.vtmEnhanced === 'true') return;


          field.dataset.vtmEnhanced = 'true';
        var btnLiberado = document.createElement('button');
        btnLiberado.type = 'button';
        btnLiberado.innerHTML = '🔓 LIBERADO';
        btnLiberado.style.cssText = 'flex:1; padding:10px 16px; border-radius:4px; font-weight:800; font-size:0.9em; cursor:pointer; font-family:"Cinzel", Georgia, serif; letter-spacing:1px; transition:all 0.25s ease; outline:none;';


          // Cria container dos botões
        function renderState(currentVal) {
           var btnGroup = document.createElement('div');
           var val = (currentVal || '').toLowerCase().trim();
           btnGroup.className = 'vtm-status-toggle-group';
           if (val === 'sim' || val === '1' || val === 'true' || val === 'liberado') {
          btnGroup.style.cssText = 'display:flex; gap:10px; margin:8px 0 12px 0; align-items:center;';
            // LIBERADO ATIVO (Verde Esmeralda)
            btnLiberado.style.background = 'linear-gradient(180deg, #1b5e20 0%, #0d3814 100%)';
            btnLiberado.style.color = '#ffffff';
            btnLiberado.style.border = '2px solid #4caf50';
            btnLiberado.style.boxShadow = '0 0 12px rgba(76, 175, 80, 0.7)';


          // Botão 1: CENSURADO
             btnCensurado.style.background = '#121218';
          var btnCensurado = document.createElement('button');
            btnCensurado.style.color = '#7a7a88';
          btnCensurado.type = 'button';
            btnCensurado.style.border = '1px solid #2d2d38';
          btnCensurado.className = 'vtm-toggle-btn vtm-btn-censurado';
            btnCensurado.style.boxShadow = 'none';
          btnCensurado.innerHTML = '⬛ CENSURADO';
          } else {
          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;';
            // CENSURADO ATIVO (Vermelho Carmesim)
 
            btnCensurado.style.background = 'linear-gradient(180deg, #5c0f18 0%, #2b060a 100%)';
          // Botão 2: LIBERADO
            btnCensurado.style.color = '#ffffff';
          var btnLiberado = document.createElement('button');
            btnCensurado.style.border = '2px solid #ff2a3f';
          btnLiberado.type = 'button';
            btnCensurado.style.boxShadow = '0 0 12px rgba(255, 42, 63, 0.7)';
          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.background = '#121218';
              btnLiberado.style.color = '#888892';
            btnLiberado.style.color = '#7a7a88';
              btnLiberado.style.border = '1px solid #333340';
            btnLiberado.style.border = '1px solid #2d2d38';
              btnLiberado.style.boxShadow = 'none';
            btnLiberado.style.boxShadow = 'none';
            }
           }
           }
        }


          // Ações de clique
        btnCensurado.onclick = function (e) {
          btnCensurado.onclick = function (e) {
          e.preventDefault();
            e.preventDefault();
          e.stopPropagation();
            input.value = 'nao';
          input.value = 'nao';
            if (window.$) { $(input).trigger('input').trigger('change'); }
          input.dispatchEvent(new Event('input', { bubbles: true }));
            updateStyles('nao');
          input.dispatchEvent(new Event('change', { bubbles: true }));
          };
          if (window.$) { $(input).trigger('input').trigger('change'); }
          renderState('nao');
        };


          btnLiberado.onclick = function (e) {
        btnLiberado.onclick = function (e) {
            e.preventDefault();
          e.preventDefault();
            input.value = 'sim';
          e.stopPropagation();
            if (window.$) { $(input).trigger('input').trigger('change'); }
          input.value = 'sim';
            updateStyles('sim');
          input.dispatchEvent(new Event('input', { bubbles: true }));
          };
          input.dispatchEvent(new Event('change', { bubbles: true }));
          if (window.$) { $(input).trigger('input').trigger('change'); }
          renderState('sim');
        };


          // Monitora alterações manuais
        input.addEventListener('input', function () {
          input.addEventListener('input', function () {
          renderState(input.value);
            updateStyles(input.value);
        });
          });


          // Estado inicial
        // Estado inicial
          updateStyles(input.value || 'nao');
        renderState(input.value || 'nao');


          // Monta na tela
        box.appendChild(btnCensurado);
          btnGroup.appendChild(btnCensurado);
        box.appendChild(btnLiberado);
          btnGroup.appendChild(btnLiberado);


          if (inputWrapper) {
        // Insere a caixa de botões antes do input e esconde o campo de texto
            inputWrapper.parentNode.insertBefore(btnGroup, inputWrapper);
        var targetWrapper = input.closest('.oo-ui-textInputWidget') || input.closest('.oo-ui-inputWidget') || input;
            // Oculta o input textual bruto para ficar 100% visual
        targetWrapper.parentNode.insertBefore(box, targetWrapper);
            inputWrapper.style.display = 'none';
        targetWrapper.style.display = 'none';
          }
       }
        }
       });
     });
     });
   }
   }


   // Observador contínuo para o modal do VisualEditor
   // Executa o scanner continuamente a cada 250ms para capturar modais OOUI dinâmicos
   var modalObserver = new MutationObserver(function () {
   setInterval(transformLiberadoInputs, 250);
    enhanceVisualEditorModal();
  });
  modalObserver.observe(document.body, { childList: true, subtree: true });


   // ------------------------------------------------------------------------
   // ------------------------------------------------------------------------
Line 192: Line 182:
     bar.appendChild(label);
     bar.appendChild(label);


    // 1. Inserir Censurado
     bar.appendChild(makeBtn(
     bar.appendChild(makeBtn(
       '⬛ Censurar Trecho',
       '⬛ Censurar Trecho',
Line 200: Line 189:
     ));
     ));


    // 2. Inserir Liberado
     bar.appendChild(makeBtn(
     bar.appendChild(makeBtn(
       '🔓 Inserir Liberado',
       '🔓 Inserir Liberado',
Line 208: Line 196:
     ));
     ));


    // 3. Dossiê Bloco
     bar.appendChild(makeBtn(
     bar.appendChild(makeBtn(
       '📁 Dossiê Bloco',
       '📁 Dossiê Bloco',
Line 216: Line 203:
     ));
     ));


    // 4. Remover Censura
     bar.appendChild(makeBtn(
     bar.appendChild(makeBtn(
       '🗑️ Remover Censura',
       '🗑️ Remover Censura',
Line 392: Line 378:
   }
   }


  // Inicialização
   function initAll() {
   function initAll() {
     initVtmSecrets();
     initVtmSecrets();
     initSectionAnimation();
     initSectionAnimation();
     initVtmQuickToolbar();
     initVtmQuickToolbar();
     enhanceVisualEditorModal();
     transformLiberadoInputs();
   }
   }



Revision as of 19:50, 1 September 2026

/* ==========================================================================
   VAMPIRO: A MÁSCARA — SCRIPTS GLOBAIS & BOTÕES INTERATIVOS CENSURADO/LIBERADO
   ========================================================================== */

(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. SCANNER UNIVERSAL DO MODAL: SUBSTITUI O CAMPO TEXTO POR [CENSURADO | LIBERADO]
  // ------------------------------------------------------------------------
  function transformLiberadoInputs() {
    // Procura todos os inputs e textareas dentro de formulários/modais OOUI
    var inputs = document.querySelectorAll('.oo-ui-windowManager input, .oo-ui-windowManager textarea, .ve-ui-surface input, .oo-ui-fieldLayout input, .oo-ui-fieldLayout textarea');
    
    inputs.forEach(function (input) {
      // Encontra o container pai (PageLayout ou FieldLayout)
      var container = input.closest('.oo-ui-fieldLayout') || 
                      input.closest('.ve-ui-mwParameterPage') || 
                      input.closest('.oo-ui-pageLayout') || 
                      input.parentElement;
      if (!container) return;

      var textCheck = (container.textContent || '').toLowerCase();
      // Verifica se o bloco se refere ao parâmetro "liberado"
      if (textCheck.indexOf('liberado') !== -1 && !container.querySelector('.vtm-custom-toggle-box')) {
        
        // Cria a caixa com os 2 botões
        var box = document.createElement('div');
        box.className = 'vtm-custom-toggle-box';
        box.style.cssText = 'display:flex; gap:12px; margin:10px 0 16px 0; width:100%; max-width:460px;';

        var btnCensurado = document.createElement('button');
        btnCensurado.type = 'button';
        btnCensurado.innerHTML = '⬛ CENSURADO';
        btnCensurado.style.cssText = 'flex:1; padding:10px 16px; border-radius:4px; font-weight:800; font-size:0.9em; cursor:pointer; font-family:"Cinzel", Georgia, serif; letter-spacing:1px; transition:all 0.25s ease; outline:none;';

        var btnLiberado = document.createElement('button');
        btnLiberado.type = 'button';
        btnLiberado.innerHTML = '🔓 LIBERADO';
        btnLiberado.style.cssText = 'flex:1; padding:10px 16px; border-radius:4px; font-weight:800; font-size:0.9em; cursor:pointer; font-family:"Cinzel", Georgia, serif; letter-spacing:1px; transition:all 0.25s ease; outline:none;';

        function renderState(currentVal) {
          var val = (currentVal || '').toLowerCase().trim();
          if (val === 'sim' || val === '1' || val === 'true' || val === 'liberado') {
            // LIBERADO ATIVO (Verde Esmeralda)
            btnLiberado.style.background = 'linear-gradient(180deg, #1b5e20 0%, #0d3814 100%)';
            btnLiberado.style.color = '#ffffff';
            btnLiberado.style.border = '2px solid #4caf50';
            btnLiberado.style.boxShadow = '0 0 12px rgba(76, 175, 80, 0.7)';

            btnCensurado.style.background = '#121218';
            btnCensurado.style.color = '#7a7a88';
            btnCensurado.style.border = '1px solid #2d2d38';
            btnCensurado.style.boxShadow = 'none';
          } else {
            // CENSURADO ATIVO (Vermelho Carmesim)
            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 12px rgba(255, 42, 63, 0.7)';

            btnLiberado.style.background = '#121218';
            btnLiberado.style.color = '#7a7a88';
            btnLiberado.style.border = '1px solid #2d2d38';
            btnLiberado.style.boxShadow = 'none';
          }
        }

        btnCensurado.onclick = function (e) {
          e.preventDefault();
          e.stopPropagation();
          input.value = 'nao';
          input.dispatchEvent(new Event('input', { bubbles: true }));
          input.dispatchEvent(new Event('change', { bubbles: true }));
          if (window.$) { $(input).trigger('input').trigger('change'); }
          renderState('nao');
        };

        btnLiberado.onclick = function (e) {
          e.preventDefault();
          e.stopPropagation();
          input.value = 'sim';
          input.dispatchEvent(new Event('input', { bubbles: true }));
          input.dispatchEvent(new Event('change', { bubbles: true }));
          if (window.$) { $(input).trigger('input').trigger('change'); }
          renderState('sim');
        };

        input.addEventListener('input', function () {
          renderState(input.value);
        });

        // Estado inicial
        renderState(input.value || 'nao');

        box.appendChild(btnCensurado);
        box.appendChild(btnLiberado);

        // Insere a caixa de botões antes do input e esconde o campo de texto
        var targetWrapper = input.closest('.oo-ui-textInputWidget') || input.closest('.oo-ui-inputWidget') || input;
        targetWrapper.parentNode.insertBefore(box, targetWrapper);
        targetWrapper.style.display = 'none';
      }
    });
  }

  // Executa o scanner continuamente a cada 250ms para capturar modais OOUI dinâmicos
  setInterval(transformLiberadoInputs, 250);

  // ------------------------------------------------------------------------
  // 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);

    bar.appendChild(makeBtn(
      '⬛ Censurar Trecho',
      'Envolve o texto com {{Segredo|...|liberado=nao}}',
      '#240c12', '#ff2a3f',
      function () { insertSecretWithStatus(textbox, 'nao'); }
    ));

    bar.appendChild(makeBtn(
      '🔓 Inserir Liberado',
      'Envolve o texto com {{Segredo|...|liberado=sim}}',
      '#0d2915', '#2e7d32',
      function () { insertSecretWithStatus(textbox, 'sim'); }
    ));

    bar.appendChild(makeBtn(
      '📁 Dossiê Bloco',
      'Insere bloco confidencial completo',
      '#181822', '#3d0a12',
      function () { insertSecretBlock(textbox); }
    ));

    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); });
      }
    });
  }

  function initAll() {
    initVtmSecrets();
    initSectionAnimation();
    initVtmQuickToolbar();
    transformLiberadoInputs();
  }

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