MediaWiki:Timeless.js:修订间差异

来自「荏苒之境」
无编辑摘要
无编辑摘要
标签已被回退
第102行: 第102行:
}
}


let siteNavigation = document.getElementById('mw-site-navigation');
window.addEventListener("load", () => {
let floatingToC;
let siteNavigation = document.getElementById('mw-site-navigation');
let toc = document.getElementById('toc');
let floatingToC;
let tocInitialParent = toc.parentNode;
let toc = document.getElementById('toc');
 
let tocInitialParent = toc.parentNode;
const userAgent = navigator.userAgent;
const isSafari = userAgent.includes("Safari");
const userAgent = navigator.userAgent;
 
const isSafari = userAgent.includes("Safari");
mw.hook('ve.newTarget').add(target => {
if (target.constructor.static.name !== 'article') {
mw.hook('ve.newTarget').add(target => {
return;
if (target.constructor.static.name !== 'article') {
return;
}
target.on('teardown', () => {
var event = new CustomEvent("pageupdate");
document.dispatchEvent(event);
});
});
const updateToCParent = () => {
let width = window.innerWidth;
if (isSafari && width > 1354 || !isSafari && width > 1339) {
if (toc.parentNode != siteNavigation) {
toc.parentNode.removeChild(toc);
siteNavigation.appendChild(toc);
}
}
else {
if (toc.parentNode == tocInitialParent || toc.parentNode == siteNavigation) {
toc.parentNode.removeChild(toc);
siteNavigation.parentNode.lastChild.before(toc);
}
}
}
}
target.on('teardown', () => {
var event = new CustomEvent("pageupdate");
window.addEventListener("resize", () => {
document.dispatchEvent(event);
if (floatingToC == null) {
return;
}
updateToCParent();
});
});
});
 
document.addEventListener('pageupdate', () => {
const updateToCParent = () => {
let newToC = document.querySelectorAll('.mw-parser-output > #toc')[0];
let width = window.innerWidth;
if (newToC == null) {
if (isSafari && width > 1354 || !isSafari && width > 1339) {
return;
if (toc.parentNode != siteNavigation) {
toc.parentNode.removeChild(toc);
siteNavigation.appendChild(toc);
}
}
}
if (floatingToC != null) {
else {
floatingToC.remove();
if (toc.parentNode == tocInitialParent || toc.parentNode == siteNavigation) {
toc.parentNode.removeChild(toc);
siteNavigation.parentNode.lastChild.before(toc);
}
}
toc = newToC;
floatingToC = new FloatingToC(toc);
updateToCParent();
});
if (toc) {
    floatingToC = new FloatingToC(toc);
    updateToCParent();
}
}
}
})
 
window.addEventListener("resize", () => {
if (floatingToC == null) {
return;
}
updateToCParent();
});
 
document.addEventListener('pageupdate', () => {
let newToC = document.querySelectorAll('.mw-parser-output > #toc')[0];
if (newToC == null) {
return;
}
if (floatingToC != null) {
floatingToC.remove();
}
toc = newToC;
floatingToC = new FloatingToC(toc);
updateToCParent();
});
 
if (toc) {
    floatingToC = new FloatingToC(toc);
    updateToCParent();
}

2026年4月6日 (一) 20:58的版本

/* 将为Timeless皮肤的用户加载此处的所有JavaScript  */

class HeadingTracker {
    constructor(rootElement) {
    	this.rootElement = rootElement
        this.headings = [];
        this.cacheExpired = true;
        this.init();
    }

    init() {
        this.collectHeadings();
        window.addEventListener('resize', () => this.cacheExpired = true);
        window.addEventListener('DOMContentLoaded', () => this.cacheExpired = true);
    }

    collectHeadings() {
        const rawHeadings = Array.from(this.rootElement.querySelectorAll('h1, h2, h3, h4, h5, h6'));
        this.headings = rawHeadings
            .map(heading => ({
                element: heading,
                top: heading.getBoundingClientRect().top + window.scrollY
            }))
            .sort((a, b) => a.top - b.top);
        this.cacheExpired = false;
    }

    getNearestHeadingAboveViewport() {
        if (this.cacheExpired) this.collectHeadings();

        const scrollY = window.scrollY + 60;
        let left = 0, right = this.headings.length - 1;
        let result = null;

        while (left <= right) {
            const mid = Math.floor((left + right) / 2);
            if (this.headings[mid].top <= scrollY) {
                result = this.headings[mid].element;
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return result;
    }
}

class FloatingToC {
	constructor(toc) {
		this.element = toc;
		this.ignoreScrollUpdate = false;
	    
	    let tocTextList = Array.from(toc.querySelectorAll(".toctext"));
	    this.tocTexts = new Map(tocTextList.map(t => {
	    	let textContent = t.textContent
	    	t.parentElement.onclick = () => {
	    		this.ignoreScrollUpdate = true;
	    		this.setCurrentHeading(textContent);
	    	};
	    	return [textContent, t];
	    }));

		this.tracker = new HeadingTracker(
			document.getElementById('mw-content-text'));
		
		this.scrollHandler = this.onScroll.bind(this);
		window.addEventListener('scroll', this.scrollHandler);
	}
	
	remove() {
		window.removeEventListener('scroll', this.scrollHandler);
		this.element.remove();
	}
	
    setCurrentHeading(heading) {
    	const lastHeading = this.lastHeading;
    	if (heading == lastHeading) {
    		return;
    	}
    	if (lastHeading) {
    		let text = this.tocTexts.get(lastHeading)
    		if (text) text.classList.remove('current-heading');
    	}
    	this.lastHeading = heading;
    	if (heading) {
    		let text = this.tocTexts.get(heading)
    		if (text) text.classList.add('current-heading');
    	}
    };
	
	onScroll() {
		if (window.innerWidth < 1100) {
			return;
		}
		if (this.ignoreScrollUpdate) {
			this.ignoreScrollUpdate = false;
			return;
		}
	    let heading = this.tracker.getNearestHeadingAboveViewport();
	    this.setCurrentHeading(heading ? heading.textContent : null);
	}
}

window.addEventListener("load", () => {
	let siteNavigation = document.getElementById('mw-site-navigation');
	let floatingToC;
	let toc = document.getElementById('toc');
	let tocInitialParent = toc.parentNode;
	
	const userAgent = navigator.userAgent;
	const isSafari = userAgent.includes("Safari");
	
	mw.hook('ve.newTarget').add(target => {
		if (target.constructor.static.name !== 'article') {
			return;
		}
		target.on('teardown', () => {
			var event = new CustomEvent("pageupdate");
			document.dispatchEvent(event);
		});
	});
	
	const updateToCParent = () => {
		let width = window.innerWidth;
		if (isSafari && width > 1354 || !isSafari && width > 1339) {
			if (toc.parentNode != siteNavigation) {
				toc.parentNode.removeChild(toc);
				siteNavigation.appendChild(toc);
			}
		}
		else {
			if (toc.parentNode == tocInitialParent || toc.parentNode == siteNavigation) {
				toc.parentNode.removeChild(toc);
				siteNavigation.parentNode.lastChild.before(toc);
			}
		}
	}
	
	window.addEventListener("resize", () => {
		if (floatingToC == null) {
			return;
		}
		updateToCParent();
	});
	
	document.addEventListener('pageupdate', () => {
		let newToC = document.querySelectorAll('.mw-parser-output > #toc')[0];
		if (newToC == null) {
			return;
		}
		if (floatingToC != null) {
			floatingToC.remove();
		}
		toc = newToC;
		floatingToC = new FloatingToC(toc);
		updateToCParent();
	});
	
	if (toc) {
	    floatingToC = new FloatingToC(toc);
	    updateToCParent();
	}
})