MediaWiki:Tweeki.js

Dari GBI Danau Bogor Raya
Revisi sejak 30 Oktober 2026 21.45 oleh Leo (bicara | kontrib) (Tweeki.js: susun ulang setiap Masonry (.grid dan data-masonry) di dalam collapse/accordion yang baru terbuka, apa pun id-nya.)
Lompat ke: navigasi, cari

Catatan: Setelah menerbitkan, Anda mungkin perlu melewati tembolok peramban web Anda untuk melihat perubahan.

  • Firefox/Safari: Tekan dan tahan Shift sembari mengeklik Reload, atau tekan Ctrl-F5 atau Ctrl-R (⌘-R di Mac)
  • Google Chrome: Tekan Ctrl-Shift-R (⌘-Shift-R di Mac)
  • Edge: Tahan Ctrl sembari mengeklik Refresh, atau tekan Ctrl-F5
/* Any JavaScript here will be loaded for users using the Tweeki skin */
/* Setiap blok dijaga: kalau elemen/library yang dicari tidak ada di halaman ini,
   blok itu dilewati. Dulu satu error menghentikan seluruh file. */

// Bootstrap 5: Tweeki tidak membuka global `bootstrap`, pakai jembatan jQuery-nya.
// Plugin jQuery-nya bisa belum terdaftar (urutan muat), jadi dicek dulu.
$(function () {
  if ($.fn.tooltip) {
    $('[data-toggle="tooltip"]').tooltip();
  }
  if ($.fn.alert) {
    $('.alert').alert();
  }
  if ($.fn.popover) {
    $('[data-bs-toggle="popover"]').popover();
  }
});

// Header tabel #table-1 menempel saat digulir (butuh #header-fixed di halaman)
(function () {
  var $table = $('#table-1');
  var $fixedHeader = $('#header-fixed');
  if (!$table.length || !$fixedHeader.length) {
    return;
  }
  var tableOffset = $table.offset().top;
  $fixedHeader.append($table.children('thead').clone());

  $(window).on('scroll', function () {
    var offset = $(this).scrollTop();

    if (offset >= tableOffset && $fixedHeader.is(':hidden')) {
      $fixedHeader.show();
    } else if (offset < tableOffset) {
      $fixedHeader.hide();
    }
  });
}());

// Pindahkan kategori ke sidebar kanan
(function () {
  var one = document.querySelector('#sidebar-right');
  var catlinks = document.querySelector('.catlinks');
  if (!one || !catlinks) {
    return;
  }
  // salin ke array dulu: .children ikut berubah saat elemen dipindah (dulu yang terpindah hanya selang-seling)
  Array.prototype.slice.call(catlinks.children).forEach(function (child) {
    one.appendChild(child);
  });
}());


// MASONRY: .grid > .grid-item. Library dimuat async dari CDN (hook BeforePageDisplay di
// LocalSettings), jadi bisa belum tersedia saat file ini jalan -> coba lagi saat window load.
// Hanya .grid yang benar-benar berisi .grid-item: badan accordion di Templat:BootstrapAccordion2Collapse
// juga berkelas .grid tanpa item, dan Masonry membuatnya setinggi 0 sehingga isinya menimpa bagian bawahnya.
var msnrys = [];

function initMasonry() {
  if (msnrys.length || typeof Masonry === 'undefined') {
    return;
  }
  Array.prototype.forEach.call(document.querySelectorAll('.grid'), function (grid) {
    if (grid.querySelector(':scope > .grid-item')) {
      msnrys.push(new Masonry(grid, {
        itemSelector: '.grid-item',
        percentPosition: true
      }));
    }
  });
}
initMasonry();
window.addEventListener('load', initMasonry);

// Daftar isi (ScrollSpy): judul berawalan "#" (mis. "#1 Kanak-kanak") atau berisi "?" menghasilkan
// href seperti "##1_Kanak-kanak" yang bukan selector CSS sah -> ScrollSpy Bootstrap error dan
// berhenti, sehingga sub-daftar H3 tidak pernah terbuka. Escape href-nya, lalu tangani kliknya
// sendiri (href yang di-escape tidak lagi cocok dengan id heading).
// ScrollSpy menyimpan tautan dengan kunci href (yang sudah di-escape) tetapi mencarinya dengan
// '#' + id heading, jadi kunci versi id ditambahkan sebagai alias (ScrollSpy Bootstrap 5.3).
$(function () {
  var $links;
  var changed = false;

  function getSpy() {
    var SS = $.fn.scrollspy && $.fn.scrollspy.Constructor;
    return SS && SS.getInstance ? SS.getInstance(document.body) : null;
  }

  function syncSpy() {
    var spy = getSpy();
    if (!spy) {
      return false;
    }
    spy.refresh();
    if (spy._targetLinks) {
      $links.each(function () {
        var id = $(this).data('tocId');
        if (id) {
          spy._targetLinks.set('#' + id, this);
        }
      });
    }
    return true;
  }

  if (!window.CSS || !CSS.escape) {
    return;
  }
  $links = $('#toc ul a[href^="#"]');
  $links.each(function () {
    var id, safe;
    try {
      id = decodeURIComponent(this.hash.slice(1));
    } catch (e) {
      return;
    }
    safe = '#' + CSS.escape(id);
    if (this.getAttribute('href') !== safe) {
      $(this).attr('href', safe).data('tocId', id);
      changed = true;
    }
  }).on('click', function (e) {
    var id = $(this).data('tocId');
    if (id && document.getElementById(id)) {
      e.preventDefault();
      // '#' tambahan wajib: setter location.hash membuang satu '#' di depan
      location.hash = '#' + id;
    }
  });
  // ScrollSpy dibuat oleh skin Tweeki, bisa sesudah blok ini jalan (event load pun sering sudah
  // lewat), jadi coba ulang beberapa kali sampai ScrollSpy ada (maks. ±5 detik)
  function trySync(tries) {
    if (!syncSpy() && tries > 0) {
      setTimeout(function () {
        trySync(tries - 1);
      }, 200);
    }
  }
  if (changed) {
    setTimeout(function () {
      trySync(25);
    }, 0);
  }
});

// bootstrap 5.3 accordion/collapse: Masonry di dalam bagian yang tertutup saat halaman dimuat
// mengukur kartu setinggi/selebar 0 -> menumpuk. Susun ulang setiap Masonry (.grid maupun
// data-masonry) di dalam bagian yang baru terbuka, apa pun id accordion-nya.
$(document).on('shown.bs.collapse', function (e) {
  if (typeof Masonry === 'undefined') {
    return;
  }
  $(e.target).find('.grid, [data-masonry]').each(function () {
    var m = Masonry.data(this);
    if (m) {
      m.layout();
    }
  });
});