Перейти до вмісту

MediaWiki:Common.js: відмінності між версіями

Аналітика: Matomo-трекер замість простого beacon (cookieless, DNT)
(Hero для міст: cityHero + page-has-hero, тулбар у hero)
(Аналітика: Matomo-трекер замість простого beacon (cookieless, DNT))
 
(Не показано 11 проміжних версій цього користувача)
Рядок 102: Рядок 102:
   document.body.style.setProperty("--page-hero-bg", 'url("' + cfg.img + '")');
   document.body.style.setProperty("--page-hero-bg", 'url("' + cfg.img + '")');
   document.body.style.setProperty("--page-hero-fg", "#ffffff");
   document.body.style.setProperty("--page-hero-fg", "#ffffff");
   if (cfg.title) {
   var heading = document.querySelector(".mw-first-heading, .firstHeading, #firstHeading");
    var h = document.querySelector(".mw-first-heading, .firstHeading, #firstHeading");
  if (heading) {
     if (h) h.textContent = cfg.title;
     if (cfg.title) heading.textContent = cfg.title;
    // Кикер «← Країна» над заголовком міста — повернення на сторінку країни.
    var parent = pageName.split("/")[0];
    if (parent && parent !== pageName && !document.querySelector(".hero-kicker")) {
      var href = (mw.util && mw.util.getUrl)
        ? mw.util.getUrl(parent)
        : (mw.config.get("wgArticlePath") || "/wiki/$1").replace("$1", encodeURIComponent(parent));
      var wrap = document.createElement("div");
      wrap.className = "hero-titlewrap";
      var kicker = document.createElement("a");
      kicker.className = "hero-kicker";
      kicker.href = href;
      kicker.textContent = "\u2190 " + parent.replace(/_/g, " ");
      heading.parentNode.insertBefore(wrap, heading);
      wrap.appendChild(kicker);
      wrap.appendChild(heading);
    }
   }
   }
});
// «← Назад» на звичайних внутрішніх сторінках (не головна, не міста, не
// службові). Повертає в історію браузера; якщо зайшли ззовні — на головну
// (беремо href лого). Гейт по cityHero, щоб не було гонки з page-has-hero.
$(function () {
  if (mw.config.get("wgNamespaceNumber") !== 0) return;
  if (mw.config.get("wgIsMainPage")) return;
  if (mw.config.get("wgAction") !== "view") return;
  if (cityHero[mw.config.get("wgPageName") || ""]) return;
  var heading = document.querySelector(".mw-first-heading, .firstHeading, #firstHeading");
  if (!heading || document.querySelector(".page-back-link")) return;
  var logo = document.querySelector(".mw-logo, .mw-logo-link, #p-logo a, .branding-box a");
  var fallback = (logo && logo.getAttribute("href")) || "/";
  var back = document.createElement("a");
  back.className = "page-back-link";
  back.href = fallback;
  back.textContent = "\u2190 Назад";
  back.addEventListener("click", function (e) {
    var ref = document.referrer || "";
    if (ref.indexOf(location.origin) === 0 && history.length > 1) {
      e.preventDefault();
      history.back();
    }
  });
  var container = heading.closest(".vector-page-titlebar, .mw-body-header, .pre-content") || heading;
  if (container.parentNode) container.parentNode.insertBefore(back, container);
  else heading.parentNode.insertBefore(back, heading);
});
});


Рядок 312: Рядок 358:
   });
   });
});
});
/* === Країнопедія: віджет «Повідомити про правку» (krai-feedback) === */
mw.loader.using(["mediawiki.util"], function () {
  $(function () {
    if (mw.config.get("wgNamespaceNumber") !== 0) return;  // тільки статті
    if (mw.config.get("wgAction") !== "view") return;        // тільки читання
    if (document.getElementById("krai-fb-btn")) return;
    var ENDPOINT = "/feedback.php";
    var loadedAt = Math.floor(Date.now() / 1000);            // ts фіксуємо на завантаженні (>3с завжди)
    var overlay = null;
    var content = document.getElementById("mw-content-text") || document.body;
    var lastSelText = "";
    // --- плаваюча кнопка (коли нічого не виділено) ---
    var btn = document.createElement("button");
    btn.id = "krai-fb-btn";
    btn.type = "button";
    btn.className = "k-btn k-btn--quiet krai-fb-btn";
    btn.textContent = "✏️ Знайшли неточність?";
    document.body.appendChild(btn);
    btn.addEventListener("click", function () { openModal(); });
    // --- мікроменю над виділеним текстом ---
    var tip = document.createElement("button");
    tip.id = "krai-fb-tip";
    tip.type = "button";
    tip.className = "k-btn k-btn--quiet krai-fb-tip";
    tip.textContent = "✏️ Запропонувати правку";
    tip.style.display = "none";
    document.body.appendChild(tip);
    function selectionInContent(sel) {
      if (!sel || sel.rangeCount === 0 || sel.isCollapsed) return false;
      return sel.anchorNode && content.contains(sel.anchorNode);
    }
    function hideTip() { tip.style.display = "none"; }
    function showTip() {
      var sel = window.getSelection();
      var text = ((sel && sel.toString()) || "").trim();
      if (!selectionInContent(sel) || text.length < 8) { hideTip(); return; }
      lastSelText = text.slice(0, 1000);
      var rect = sel.getRangeAt(0).getBoundingClientRect();
      if (!rect || (!rect.width && !rect.height)) { hideTip(); return; }
      tip.style.display = "block";
      var vw = document.documentElement.clientWidth;
      var top = rect.top + window.scrollY - tip.offsetHeight - 8;
      var left = rect.left + window.scrollX + (rect.width / 2) - (tip.offsetWidth / 2);
      left = Math.max(window.scrollX + 8, Math.min(left, window.scrollX + vw - tip.offsetWidth - 8));
      if (top < window.scrollY + 4) top = rect.bottom + window.scrollY + 8; // немає місця зверху → знизу
      tip.style.top = top + "px";
      tip.style.left = left + "px";
    }
    document.addEventListener("mouseup", function () { setTimeout(showTip, 10); });
    document.addEventListener("touchend", function () { setTimeout(showTip, 10); });
    document.addEventListener("selectionchange", function () {
      var sel = window.getSelection();
      if (!sel || sel.isCollapsed || !((sel.toString() || "").trim())) hideTip();
    });
    window.addEventListener("scroll", hideTip, true);
    window.addEventListener("resize", hideTip);
    tip.addEventListener("mousedown", function (e) { e.preventDefault(); }); // не втратити виділення
    tip.addEventListener("click", function (e) {
      e.preventDefault();
      hideTip();
      openModal(lastSelText);
    });
    function buildModal() {
      overlay = document.createElement("div");
      overlay.className = "krai-fb-overlay";
      overlay.innerHTML =
        '<div class="k-dialog krai-fb-modal" role="dialog" aria-modal="true" aria-labelledby="krai-fb-title">' +
          '<button type="button" class="krai-fb-close" aria-label="Закрити">×</button>' +
          '<h2 id="krai-fb-title" class="krai-fb-title">Запропонувати правку</h2>' +
          '<p class="krai-fb-sub">Помітили неточність або застарілі дані? Напишіть — ми перевіримо й оновимо. Дякуємо 💛</p>' +
          '<form class="krai-fb-form k-form">' +
            '<div class="k-field">' +
              '<label class="k-label">Цитата / фрагмент <span>(необов’язково)</span></label>' +
              '<textarea class="k-textarea" name="fragment" rows="2" placeholder="Місце, якого стосується правка"></textarea>' +
            '</div>' +
            '<div class="k-field">' +
              '<label class="k-label">Що не так або що виправити?</label>' +
              '<textarea class="k-textarea" name="comment" rows="4" placeholder="Опишіть коротко"></textarea>' +
            '</div>' +
            '<div class="k-field">' +
              '<label class="k-label">Контакт <span>(необов’язково, якщо треба відповідь)</span></label>' +
              '<input type="text" class="k-input" name="contact" placeholder="Email або @Telegram">' +
            '</div>' +
            '<input type="text" name="website" class="krai-fb-hp" tabindex="-1" autocomplete="off" aria-hidden="true">' +
            '<div class="krai-fb-actions">' +
              '<button type="button" class="k-btn k-btn--quiet krai-fb-cancel">Скасувати</button>' +
              '<button type="submit" class="k-btn k-btn--primary krai-fb-submit">Надіслати</button>' +
            '</div>' +
            '<div class="krai-fb-msg" aria-live="polite"></div>' +
          '</form>' +
        '</div>';
      document.body.appendChild(overlay);
      overlay.addEventListener("click", function (e) { if (e.target === overlay) closeModal(); });
      overlay.querySelector(".krai-fb-close").addEventListener("click", closeModal);
      overlay.querySelector(".krai-fb-cancel").addEventListener("click", closeModal);
      overlay.querySelector(".krai-fb-form").addEventListener("submit", onSubmit);
    }
    function openModal(presetFragment) {
      if (!overlay) buildModal();
      var f = overlay.querySelector('textarea[name="fragment"]');
      var sel = presetFragment || ((window.getSelection && window.getSelection().toString()) || "").trim();
      if (sel && f && !f.value) f.value = sel.slice(0, 1000);
      overlay.classList.add("is-open");
      document.addEventListener("keydown", onEsc);
      var c = overlay.querySelector('textarea[name="comment"]');
      if (c) c.focus();
    }
    function closeModal() {
      if (overlay) overlay.classList.remove("is-open");
      document.removeEventListener("keydown", onEsc);
    }
    function onEsc(e) { if (e.key === "Escape") closeModal(); }
    function onSubmit(e) {
      e.preventDefault();
      var form = e.target;
      var msg = form.querySelector(".krai-fb-msg");
      var comment = form.comment.value.trim();
      var fragment = form.fragment.value.trim();
      if (comment.length < 5 && !fragment) {
        msg.className = "krai-fb-msg is-err";
        msg.textContent = "Напишіть, будь ласка, кілька слів про правку.";
        return;
      }
      var submitBtn = form.querySelector(".krai-fb-submit");
      submitBtn.disabled = true;
      msg.className = "krai-fb-msg";
      msg.textContent = "Надсилаю…";
      var data = new FormData();
      data.append("page", mw.config.get("wgPageName") || "");
      data.append("url", location.href);
      data.append("kind", "fix");
      data.append("fragment", fragment);
      data.append("comment", comment);
      data.append("contact", form.contact.value.trim());
      data.append("website", form.website.value);
      data.append("ts", String(loadedAt));
      fetch(ENDPOINT, { method: "POST", body: data, credentials: "same-origin" })
        .then(function (r) { return r.json().catch(function () { return { ok: r.ok }; }); })
        .then(function (res) {
          if (res && res.ok) {
            form.reset();
            msg.className = "krai-fb-msg is-ok";
            msg.textContent = "Дякуємо! Ми перевіримо й оновимо. 💛";
            setTimeout(closeModal, 1800);
          } else {
            throw new Error((res && res.error) || "err");
          }
        })
        .catch(function () {
          msg.className = "krai-fb-msg is-err";
          msg.textContent = "Не вдалося надіслати. Спробуйте ще раз трохи згодом.";
        })
        .then(function () { submitBtn.disabled = false; });
    }
  });
});
/* === /krai-feedback === */
/* === Країнопедія: Matomo (self-host /matomo/, cookieless, поважає DNT) === */
var _paq = window._paq = window._paq || [];
_paq.push(["disableCookies"]);
_paq.push(["setDoNotTrack", true]);
_paq.push(["trackPageView"]);
_paq.push(["enableLinkTracking"]);
(function () {
  var u = "//nomadica.uno/matomo/";
  _paq.push(["setTrackerUrl", u + "matomo.php"]);
  _paq.push(["setSiteId", "1"]);
  var d = document, g = d.createElement("script"), s = d.getElementsByTagName("script")[0];
  g.async = true; g.src = u + "matomo.js"; s.parentNode.insertBefore(g, s);
})();
/* === /Matomo === */