MediaWiki: Tweeki.js: Perbedaan antara revisi
Dari GBI Danau Bogor Raya
Perbaiki parse error baris 40 ("/ /" -> "//"); jaga #myAccordion dan msnry yang tidak ada |
Tweeki.js: ganti penanda bagian aktif di daftar isi (ScrollSpy Bootstrap menandai judul berikutnya bila bagian pendek); judul yang diklik tetap aktif. |
||
| (9 revisi perantara oleh pengguna yang sama tidak ditampilkan) | |||
| Baris 1: | Baris 1: | ||
/* Any JavaScript here will be loaded for users using the Tweeki skin */ | /* 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 () { | $(function () { | ||
$('[data-toggle="tooltip"]').tooltip() | 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(); | |||
var | |||
$( | 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 = []; | |||
Array.prototype.forEach.call( | 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). | |||
// | |||
// Penanda bagian aktif juga tidak lagi memakai ScrollSpy: ScrollSpy hanya mengamati judul (bukan | |||
// seluruh bagian) dengan rootMargin "70px 0px -30%", jadi bila bagian yang diklik pendek, judul | |||
// berikutnya ikut terlihat dan dianggap aktif, dan saat judul keluar layar penandanya hilang. | |||
// Gantinya: bagian aktif = judul terakhir yang sudah melewati garis LINE di bawah navbar; judul yang | |||
// baru diklik tetap aktif selama masih terlihat (judul di ujung halaman tidak bisa naik ke atas). | |||
$(function () { | |||
var LINE = 120; | |||
var $links; | |||
var items = []; | |||
var current = null; | |||
var clicked = null; | |||
var clickedAt = 0; | |||
var ticking = false; | |||
// | function activate(link) { | ||
if (link === current) { | |||
return; | |||
} | |||
current = link; | |||
$('#toc a.active').removeClass('active'); | |||
if (link) { | |||
// tautan induk ikut aktif supaya sub-daftarnya (.active + ul) tetap terbuka | |||
$(link).addClass('active').parents('li').children('a').addClass('active'); | |||
} | |||
} | |||
var | function update() { | ||
var i, top, pick = null; | |||
var line = LINE; | |||
}); | var doc = document.documentElement; | ||
ticking = false; | |||
// sudah mentok di bawah: judul terakhir yang terlihat dianggap aktif (tidak bisa naik ke garis) | |||
if (window.innerHeight + window.pageYOffset >= doc.scrollHeight - 2) { | |||
line = window.innerHeight; | |||
} | |||
if (clicked) { | |||
top = clicked.el.getBoundingClientRect().top; | |||
// selama ±1 detik setelah klik halaman mungkin masih bergulir menuju judulnya | |||
if ((top >= 0 && top < window.innerHeight) || Date.now() - clickedAt < 1000) { | |||
activate(clicked.link); | |||
return; | |||
} | |||
clicked = null; | |||
} | |||
for (i = 0; i < items.length; i++) { | |||
if (items[i].el.getBoundingClientRect().top > line) { | |||
break; | |||
} | |||
pick = items[i].link; | |||
} | |||
activate(pick); | |||
} | |||
function find(id) { | |||
for (var i = 0; i < items.length; i++) { | |||
if (items[i].el.id === id) { | |||
return items[i]; | |||
} | |||
} | |||
return null; | |||
} | |||
// ScrollSpy dibuat oleh skin Tweeki, bisa sesudah blok ini jalan, jadi coba beberapa kali (±5 detik) | |||
function stopSpy(tries) { | |||
var SS = $.fn.scrollspy && $.fn.scrollspy.Constructor; | |||
var spy = SS && SS.getInstance ? SS.getInstance(document.body) : null; | |||
if (spy) { | |||
spy.dispose(); | |||
current = null; | |||
update(); | |||
} else if (tries > 0) { | |||
setTimeout(function () { | |||
stopSpy(tries - 1); | |||
}, 200); | |||
} | |||
} | |||
$links = $('#toc ul a[href^="#"]'); | |||
$links.each(function () { | |||
var | var id = $(this).data('tocId'); | ||
var el; | |||
try { | |||
id = id || decodeURIComponent(this.hash.slice(1)); | |||
if ( | } catch (e) { | ||
return; | |||
} | |||
el = document.getElementById(id); | |||
if (el) { | |||
items.push({ link: this, el: el }); | |||
} | |||
}); | }); | ||
} | if (!items.length) { | ||
return; | |||
} | |||
// href tetap di-escape: ScrollSpy Tweeki dibuat dulu sebelum dimatikan dan error pada selector tidak sah | |||
if (window.CSS && CSS.escape) { | |||
items.forEach(function (item) { | |||
var safe = '#' + CSS.escape(item.el.id); | |||
if (item.link.getAttribute('href') !== safe) { | |||
$(item.link).attr('href', safe).data('tocId', item.el.id); | |||
} | |||
}); | |||
} | |||
$.each(items, function (i, item) { | |||
$(item.link).on('click', function (e) { | |||
if ($(this).data('tocId')) { | |||
e.preventDefault(); | |||
// '#' tambahan wajib: setter location.hash membuang satu '#' di depan | |||
location.hash = '#' + item.el.id; | |||
} | |||
clicked = item; | |||
clickedAt = Date.now(); | |||
update(); | |||
}); | |||
}); | |||
// | // halaman dibuka lewat tautan langsung ke suatu bagian (mis. .../Halaman#Judul) | ||
if (location.hash) { | |||
try { | |||
clicked = find(decodeURIComponent(location.hash.slice(1))); | |||
clickedAt = Date.now(); | |||
} catch (e) {} | |||
} | |||
$(window).on('scroll resize', function () { | |||
if (!ticking) { | |||
ticking = true; | |||
window.requestAnimationFrame(update); | |||
} | |||
}); | |||
update(); | |||
setTimeout(function () { | |||
stopSpy(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(); | |||
} | |||
}); | |||
}); | }); | ||
Revisi terkini sejak 2 November 2026 11.04
/* 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).
//
// Penanda bagian aktif juga tidak lagi memakai ScrollSpy: ScrollSpy hanya mengamati judul (bukan
// seluruh bagian) dengan rootMargin "70px 0px -30%", jadi bila bagian yang diklik pendek, judul
// berikutnya ikut terlihat dan dianggap aktif, dan saat judul keluar layar penandanya hilang.
// Gantinya: bagian aktif = judul terakhir yang sudah melewati garis LINE di bawah navbar; judul yang
// baru diklik tetap aktif selama masih terlihat (judul di ujung halaman tidak bisa naik ke atas).
$(function () {
var LINE = 120;
var $links;
var items = [];
var current = null;
var clicked = null;
var clickedAt = 0;
var ticking = false;
function activate(link) {
if (link === current) {
return;
}
current = link;
$('#toc a.active').removeClass('active');
if (link) {
// tautan induk ikut aktif supaya sub-daftarnya (.active + ul) tetap terbuka
$(link).addClass('active').parents('li').children('a').addClass('active');
}
}
function update() {
var i, top, pick = null;
var line = LINE;
var doc = document.documentElement;
ticking = false;
// sudah mentok di bawah: judul terakhir yang terlihat dianggap aktif (tidak bisa naik ke garis)
if (window.innerHeight + window.pageYOffset >= doc.scrollHeight - 2) {
line = window.innerHeight;
}
if (clicked) {
top = clicked.el.getBoundingClientRect().top;
// selama ±1 detik setelah klik halaman mungkin masih bergulir menuju judulnya
if ((top >= 0 && top < window.innerHeight) || Date.now() - clickedAt < 1000) {
activate(clicked.link);
return;
}
clicked = null;
}
for (i = 0; i < items.length; i++) {
if (items[i].el.getBoundingClientRect().top > line) {
break;
}
pick = items[i].link;
}
activate(pick);
}
function find(id) {
for (var i = 0; i < items.length; i++) {
if (items[i].el.id === id) {
return items[i];
}
}
return null;
}
// ScrollSpy dibuat oleh skin Tweeki, bisa sesudah blok ini jalan, jadi coba beberapa kali (±5 detik)
function stopSpy(tries) {
var SS = $.fn.scrollspy && $.fn.scrollspy.Constructor;
var spy = SS && SS.getInstance ? SS.getInstance(document.body) : null;
if (spy) {
spy.dispose();
current = null;
update();
} else if (tries > 0) {
setTimeout(function () {
stopSpy(tries - 1);
}, 200);
}
}
$links = $('#toc ul a[href^="#"]');
$links.each(function () {
var id = $(this).data('tocId');
var el;
try {
id = id || decodeURIComponent(this.hash.slice(1));
} catch (e) {
return;
}
el = document.getElementById(id);
if (el) {
items.push({ link: this, el: el });
}
});
if (!items.length) {
return;
}
// href tetap di-escape: ScrollSpy Tweeki dibuat dulu sebelum dimatikan dan error pada selector tidak sah
if (window.CSS && CSS.escape) {
items.forEach(function (item) {
var safe = '#' + CSS.escape(item.el.id);
if (item.link.getAttribute('href') !== safe) {
$(item.link).attr('href', safe).data('tocId', item.el.id);
}
});
}
$.each(items, function (i, item) {
$(item.link).on('click', function (e) {
if ($(this).data('tocId')) {
e.preventDefault();
// '#' tambahan wajib: setter location.hash membuang satu '#' di depan
location.hash = '#' + item.el.id;
}
clicked = item;
clickedAt = Date.now();
update();
});
});
// halaman dibuka lewat tautan langsung ke suatu bagian (mis. .../Halaman#Judul)
if (location.hash) {
try {
clicked = find(decodeURIComponent(location.hash.slice(1)));
clickedAt = Date.now();
} catch (e) {}
}
$(window).on('scroll resize', function () {
if (!ticking) {
ticking = true;
window.requestAnimationFrame(update);
}
});
update();
setTimeout(function () {
stopSpy(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();
}
});
});