Toggle menu
Toggle preferences menu
Toggle personal menu
Nejste přihlášen(a)
Your IP address will be publicly visible if you make any edits.

MediaWiki:Common.js: Porovnání verzí

MediaWiki interface page
Bez shrnutí editace
Bez shrnutí editace
Řádek 6: Řádek 6:
});
});


// Mini toolbar pro formátování textu v parametrech šablon (VisualEditor)
(function () {
(function () {
   function wrapSelection(input, before, after) {
   function replaceRange(input, start, end, str) {
     const s = input.selectionStart, e = input.selectionEnd;
     const v = input.value;
    const val = input.value, sel = val.slice(s, e);
     input.value = v.slice(0, start) + str + v.slice(end);
     input.value = val.slice(0, s) + before + sel + after + val.slice(e);
     const pos = start + str.length;
     const pos = s + before.length + sel.length + after.length;
     input.focus();
     input.focus();
     input.setSelectionRange(pos, pos);
     input.setSelectionRange(pos, pos);
   }
   }
   function wrapEachLine(input, prefix) {
 
   function toggleWrap(input, before, after) {
    const s = input.selectionStart, e = input.selectionEnd;
    const v = input.value;
 
    const hasBefore = s >= before.length && v.slice(s - before.length, s) === before;
    const hasAfter  = v.slice(e, e + after.length) === after;
 
    if (hasBefore && hasAfter) {
      const inner = v.slice(s, e);
      input.value = v.slice(0, s - before.length) + inner + v.slice(e + after.length);
      const newS = s - before.length;
      const newE = newS + inner.length;
      input.focus();
      input.setSelectionRange(newS, newE);
    } else {
      input.value = v.slice(0, s) + before + v.slice(s, e) + after + v.slice(e);
      const pos = e + before.length + after.length;
      input.focus();
      input.setSelectionRange(pos, pos);
    }
  }
 
  function getLineRange(input) {
    const v = input.value;
    const s = input.selectionStart, e = input.selectionEnd;
    const start = v.lastIndexOf('\n', s - 1) + 1;
    const end = (v.indexOf('\n', e) + 1) || v.length;
    return { start, end, text: v.slice(start, end) };
  }
  function setLines(input, range, newText) {
    replaceRange(input, range.start, range.end, newText);
  }
 
  function toggleBullets(input) {
    const r = getLineRange(input);
    const lines = r.text.replace(/\n$/, '').split('\n');
    const allBulleted = lines.filter(l => l.trim() !== '').every(l => l.startsWith('* '));
    const next = lines.map(l => {
      if (l.trim() === '') return l;
      return allBulleted ? l.replace(/^\* /, '') : ('* ' + l);
    }).join('\n') + (r.text.endsWith('\n') ? '\n' : '');
    setLines(input, r, next);
  }
 
  function stripAnyHeading(line) {
    const m = line.match(/^\s*(=+)\s*(.*?)\s*(=+)\s*$/);
    if (m && m[1] && m[3]) return m[2];
    return line;
  }
  function toggleHeading(input, level) {
    const r = getLineRange(input);
    const open = '='.repeat(level) + ' ';
    const close = ' ' + '='.repeat(level);
 
    const lines = r.text.replace(/\n$/, '').split('\n');
    const next = lines.map(l => {
      if (l.trim() === '') return l;
      const isThis =
        l.startsWith(open) && l.trimEnd().endsWith(close);
      if (isThis) {
        return stripAnyHeading(l);
      }
      return open + stripAnyHeading(l) + close;
    }).join('\n') + (r.text.endsWith('\n') ? '\n' : '');
    setLines(input, r, next);
  }
 
  function insertLink(input) {
     const s = input.selectionStart, e = input.selectionEnd;
     const s = input.selectionStart, e = input.selectionEnd;
     const val = input.value, sel = val.slice(s, e);
     const v = input.value;
    const wrapped = (sel || '').split('\n').map(l => (l ? prefix + l : l)).join('\n');
    const sel = v.slice(s, e) || 'Cíl';
     input.value = val.slice(0, s) + wrapped + val.slice(e);
     input.value = v.slice(0, s) + '[[' + sel + '|' + sel + ']]' + v.slice(e);
     const pos = s + wrapped.length;
     const pos = s + 4 + sel.length + 1 + sel.length + 2;
     input.focus();
     input.focus();
     input.setSelectionRange(pos, pos);
     input.setSelectionRange(pos, pos);
   }
   }
  function addToolbarNear(input) {
    const $input = $(input);


    // najdi pole (field) – co nejvýš, ale v rámci dialogu
  function addToolbarForPage(pageEl) {
     const $field =
     const $page = $(pageEl);
      $input.closest('.ve-ui-mwParameterPage-field').length ?
    if ($page.data('privateToolbar')) return;
      $input.closest('.ve-ui-mwParameterPage-field') :
      $input.closest('.oo-ui-layout'); // fallback


     if (!$field.length || $field.data('privateToolbar')) return;
    const $textarea = $page.find('textarea.oo-ui-inputWidget-input').first();
     if (!$textarea.length) return;
    const input = $textarea.get(0);
 
    $page.addClass('private-wikitext');


     const $bar = $('<div class="private-mini-toolbar" />');
     const $bar = $('<div class="private-mini-toolbar" />');
     function btn(label, title, handler) {
     function btn(label, title, handler) {
       $('<button type="button" class="pmt-btn" />')
       $('<button type="button" class="pmt-btn" />')
         .text(label).attr('title', title).on('click', () => handler(input))
         .text(label)
        .attr('title', title)
        .on('click', () => handler(input))
         .appendTo($bar);
         .appendTo($bar);
     }
     }
    btn('B',  "Tučné ('''...''')",  el => wrapSelection(el, "'''", "'''"));
    btn('I',  "Kurzíva (''...'' )",  el => wrapSelection(el, "''",  "''"));
    btn('H2',  'Nadpis H2',          el => wrapSelection(el, '== ', ' =='));
    btn('•',  'Seznam (odrážky)',    el => wrapEachLine(el, '* '));
    btn('[]',  'Odkaz [[...]]',      el => {
      const s = el.selectionStart, e = el.selectionEnd;
      const val = el.value, sel = val.slice(s, e) || 'Cíl';
      // [[Cíl|text]] pokud je něco vybráno; jinak jen [[Cíl]]
      if (sel && sel !== 'Cíl') wrapSelection(el, '[[', '|' + sel + ']]');
      else wrapSelection(el, '[[Cíl]]', '');
    });


     // vlož lištu těsně před widget s textareou
     btn('B',  "Tučné (toggle: '''…''')",  el => toggleWrap(el, "'''", "'''"));
     const $widget = $input.closest('.oo-ui-widget');
    btn('I',  "Kurzíva (toggle: ''…'')", el => toggleWrap(el, "''", "''"));
     ($widget.length ? $widget : $field).before($bar);
    btn('H2', 'Nadpis H2 (toggle)',      el => toggleHeading(el, 2));
    btn('H3', 'Nadpis H3 (toggle)',      el => toggleHeading(el, 3));
     btn('H4', 'Nadpis H4 (toggle)',      el => toggleHeading(el, 4));
    btn('H5', 'Nadpis H5 (toggle)',      el => toggleHeading(el, 5));
     btn('•',  'Seznam odrážek (toggle)', el => toggleBullets(el));
    btn('[]', 'Odkaz [[…]]',              el => insertLink(el));


     $field.data('privateToolbar', true);
     $textarea.closest('.oo-ui-widget').before($bar);
 
    $page.data('privateToolbar', true);
  }
 
  function scan(root) {
    $(root)
      .find('.ve-ui-mwTemplateDialog .ve-ui-mwParameterPage[data-param-name="text"]')
      .each((_, el) => addToolbarForPage(el));
  }
 
  function init() {
    const mo = new MutationObserver(muts => muts.forEach(m => scan(m.target)));
    mo.observe(document.body, { childList: true, subtree: true });
    scan(document);
   }
   }


   // Delegovaný listener: když zaostří textarea v dialogu šablony, přidej lištu
   if (mw.loader.getState('ext.visualEditor.desktopArticleTarget.init')) {
  $(document).on(
     init();
     'focusin',
  } else {
     '.ve-ui-mwTemplateDialog textarea.oo-ui-inputWidget-input:not(.oo-ui-element-hidden)',
     mw.loader.using('ext.visualEditor.desktopArticleTarget.init').then(init);
    function () { addToolbarNear(this); }
   }
   );
})();
})();

Verze z 9. 10. 2025, 23:10

mw.hook('wikipage.content').add(function ($c) {
  var $ph = $c.find('.private-placeholder');
  if ($ph.length > 1) {
    $ph.slice(1).remove();
  }
});

(function () {
  function replaceRange(input, start, end, str) {
    const v = input.value;
    input.value = v.slice(0, start) + str + v.slice(end);
    const pos = start + str.length;
    input.focus();
    input.setSelectionRange(pos, pos);
  }

  function toggleWrap(input, before, after) {
    const s = input.selectionStart, e = input.selectionEnd;
    const v = input.value;

    const hasBefore = s >= before.length && v.slice(s - before.length, s) === before;
    const hasAfter  = v.slice(e, e + after.length) === after;

    if (hasBefore && hasAfter) {
      const inner = v.slice(s, e);
      input.value = v.slice(0, s - before.length) + inner + v.slice(e + after.length);
      const newS = s - before.length;
      const newE = newS + inner.length;
      input.focus();
      input.setSelectionRange(newS, newE);
    } else {
      input.value = v.slice(0, s) + before + v.slice(s, e) + after + v.slice(e);
      const pos = e + before.length + after.length;
      input.focus();
      input.setSelectionRange(pos, pos);
    }
  }

  function getLineRange(input) {
    const v = input.value;
    const s = input.selectionStart, e = input.selectionEnd;
    const start = v.lastIndexOf('\n', s - 1) + 1;
    const end = (v.indexOf('\n', e) + 1) || v.length;
    return { start, end, text: v.slice(start, end) };
  }
  function setLines(input, range, newText) {
    replaceRange(input, range.start, range.end, newText);
  }

  function toggleBullets(input) {
    const r = getLineRange(input);
    const lines = r.text.replace(/\n$/, '').split('\n');
    const allBulleted = lines.filter(l => l.trim() !== '').every(l => l.startsWith('* '));
    const next = lines.map(l => {
      if (l.trim() === '') return l;
      return allBulleted ? l.replace(/^\* /, '') : ('* ' + l);
    }).join('\n') + (r.text.endsWith('\n') ? '\n' : '');
    setLines(input, r, next);
  }

  function stripAnyHeading(line) {
    const m = line.match(/^\s*(=+)\s*(.*?)\s*(=+)\s*$/);
    if (m && m[1] && m[3]) return m[2];
    return line;
  }
  function toggleHeading(input, level) {
    const r = getLineRange(input);
    const open = '='.repeat(level) + ' ';
    const close = ' ' + '='.repeat(level);

    const lines = r.text.replace(/\n$/, '').split('\n');
    const next = lines.map(l => {
      if (l.trim() === '') return l;
      const isThis =
        l.startsWith(open) && l.trimEnd().endsWith(close);
      if (isThis) {
        return stripAnyHeading(l);
      }
      return open + stripAnyHeading(l) + close;
    }).join('\n') + (r.text.endsWith('\n') ? '\n' : '');
    setLines(input, r, next);
  }

  function insertLink(input) {
    const s = input.selectionStart, e = input.selectionEnd;
    const v = input.value;
    const sel = v.slice(s, e) || 'Cíl';
    input.value = v.slice(0, s) + '[[' + sel + '|' + sel + ']]' + v.slice(e);
    const pos = s + 4 + sel.length + 1 + sel.length + 2;
    input.focus();
    input.setSelectionRange(pos, pos);
  }

  function addToolbarForPage(pageEl) {
    const $page = $(pageEl);
    if ($page.data('privateToolbar')) return;

    const $textarea = $page.find('textarea.oo-ui-inputWidget-input').first();
    if (!$textarea.length) return;
    const input = $textarea.get(0);

    $page.addClass('private-wikitext');

    const $bar = $('<div class="private-mini-toolbar" />');

    function btn(label, title, handler) {
      $('<button type="button" class="pmt-btn" />')
        .text(label)
        .attr('title', title)
        .on('click', () => handler(input))
        .appendTo($bar);
    }

    btn('B',  "Tučné (toggle: '''…''')",  el => toggleWrap(el, "'''", "'''"));
    btn('I',  "Kurzíva (toggle: ''…'')", el => toggleWrap(el, "''", "''"));
    btn('H2', 'Nadpis H2 (toggle)',      el => toggleHeading(el, 2));
    btn('H3', 'Nadpis H3 (toggle)',      el => toggleHeading(el, 3));
    btn('H4', 'Nadpis H4 (toggle)',      el => toggleHeading(el, 4));
    btn('H5', 'Nadpis H5 (toggle)',      el => toggleHeading(el, 5));
    btn('•',  'Seznam odrážek (toggle)', el => toggleBullets(el));
    btn('[]', 'Odkaz [[…]]',              el => insertLink(el));

    $textarea.closest('.oo-ui-widget').before($bar);

    $page.data('privateToolbar', true);
  }

  function scan(root) {
    $(root)
      .find('.ve-ui-mwTemplateDialog .ve-ui-mwParameterPage[data-param-name="text"]')
      .each((_, el) => addToolbarForPage(el));
  }

  function init() {
    const mo = new MutationObserver(muts => muts.forEach(m => scan(m.target)));
    mo.observe(document.body, { childList: true, subtree: true });
    scan(document);
  }

  if (mw.loader.getState('ext.visualEditor.desktopArticleTarget.init')) {
    init();
  } else {
    mw.loader.using('ext.visualEditor.desktopArticleTarget.init').then(init);
  }
})();