미디어위키:Gadget-Vector.js: 두 판 사이의 차이

식물 vs 좀비 위키
둘러보기로 이동 검색으로 이동
편집 요약 없음
태그: 되돌려진 기여
편집 요약 없음
태그: 되돌려진 기여
255번째 줄: 255번째 줄:
'use strict';
'use strict';


var MAX_RELATED = 5;
function removeRelatedInterface() {
var loaded = false;
var ids = ['pvz-related-toggle', 'pvz-related-menu'];
var loading = false;
 
function clearV24Interface() {
var ids = [
'pvz-right-panel',
'pvz-left-panel-toggle',
'pvz-right-panel-toggle'
];


ids.forEach(function (id) {
ids.forEach(function (id) {
273번째 줄: 265번째 줄:
}
}
});
});
document.documentElement.classList.remove(
'pvz-font-small',
'pvz-font-normal',
'pvz-font-large',
'pvz-width-narrow',
'pvz-width-normal',
'pvz-width-wide',
'pvz-left-panel-open',
'pvz-right-panel-open'
);
}
}


317번째 줄: 298번째 줄:
}
}


function syncSearchTop() {
function syncSearchLeft() {
var personal = document.querySelector('.skin-vector-legacy #p-personal');
var content = document.querySelector('.skin-vector-legacy #content');
var top;
var left;


if (!personal) {
if (!content) {
return;
}
 
top = personal.getBoundingClientRect().bottom + window.scrollY + 5;
document.documentElement.style.setProperty('--pvz-search-top', top + 'px');
}
 
function makeRelatedInterface() {
var button = document.getElementById('pvz-related-toggle');
var menu = document.getElementById('pvz-related-menu');
var lines;
var title;
var list;
 
if (!document.body) {
return;
}
 
if (!button) {
button = document.createElement('button');
button.id = 'pvz-related-toggle';
button.type = 'button';
button.setAttribute('aria-label', '관련 문서 열기');
button.setAttribute('title', '관련 문서');
button.setAttribute('aria-expanded', 'false');
 
lines = document.createElement('span');
lines.className = 'pvz-related-toggle-lines';
lines.setAttribute('aria-hidden', 'true');
button.appendChild(lines);
 
document.body.appendChild(button);
}
 
if (!menu) {
menu = document.createElement('aside');
menu.id = 'pvz-related-menu';
menu.setAttribute('aria-label', '관련 문서');
 
title = document.createElement('h2');
title.className = 'pvz-related-title';
title.textContent = '관련 문서';
 
list = document.createElement('ul');
list.className = 'pvz-related-list';
 
menu.appendChild(title);
menu.appendChild(list);
document.body.appendChild(menu);
}
 
if (button.getAttribute('data-pvz-ready') !== 'true') {
button.setAttribute('data-pvz-ready', 'true');
 
button.addEventListener('click', function () {
var willOpen = !menu.classList.contains('is-open');
 
menu.classList.toggle('is-open', willOpen);
button.setAttribute('aria-expanded', willOpen ? 'true' : 'false');
button.setAttribute(
'aria-label',
willOpen ? '관련 문서 닫기' : '관련 문서 열기'
);
 
if (willOpen && !loaded && !loading) {
loadRelatedPages(menu);
}
});
 
document.addEventListener('click', function (event) {
if (
menu.classList.contains('is-open') &&
!menu.contains(event.target) &&
!button.contains(event.target)
) {
menu.classList.remove('is-open');
button.setAttribute('aria-expanded', 'false');
button.setAttribute('aria-label', '관련 문서 열기');
}
});
}
}
 
function setRelatedMessage(menu, message) {
var list = menu.querySelector('.pvz-related-list');
var item = document.createElement('li');
var text = document.createElement('span');
 
list.textContent = '';
text.className = 'pvz-related-empty';
text.textContent = message;
item.appendChild(text);
list.appendChild(item);
}
 
function renderRelatedPages(menu, titles) {
var list = menu.querySelector('.pvz-related-list');
 
list.textContent = '';
 
titles.slice(0, MAX_RELATED).forEach(function (title) {
var item = document.createElement('li');
var link = document.createElement('a');
 
link.href = mw.util.getUrl(title);
link.textContent = title;
item.appendChild(link);
list.appendChild(item);
});
 
if (!list.children.length) {
setRelatedMessage(menu, '관련 문서가 없습니다.');
}
 
loaded = true;
loading = false;
}
 
function extractLinkedTitles(data) {
var pages = data && data.query && data.query.pages;
var currentTitle = mw.config.get('wgPageName').replace(/_/g, ' ');
var titles = [];
var seen = Object.create(null);
 
if (!pages) {
return titles;
}
 
Object.keys(pages).some(function (pageId) {
var links = pages[pageId].links || [];
 
links.forEach(function (link) {
var title = link.title;
 
if (
title &&
title !== currentTitle &&
!seen[title] &&
titles.length < MAX_RELATED
) {
seen[title] = true;
titles.push(title);
}
});
 
return titles.length >= MAX_RELATED;
});
 
return titles;
}
 
function searchFallback(menu) {
var pageTitle = mw.config.get('wgTitle') || '';
 
return new mw.Api().get({
action: 'query',
list: 'search',
srsearch: pageTitle,
srnamespace: 0,
srlimit: MAX_RELATED + 1,
formatversion: 2
}).then(function (data) {
var currentTitle = mw.config.get('wgPageName').replace(/_/g, ' ');
var results = data && data.query && data.query.search
? data.query.search
: [];
var titles = results
.map(function (result) {
return result.title;
})
.filter(function (title) {
return title && title !== currentTitle;
})
.slice(0, MAX_RELATED);
 
renderRelatedPages(menu, titles);
});
}
 
function loadRelatedPages(menu) {
var articleId = mw.config.get('wgArticleId');
 
loading = true;
setRelatedMessage(menu, '관련 문서를 불러오는 중…');
 
if (!window.mw || !mw.Api || !mw.util) {
setRelatedMessage(menu, '관련 문서를 불러올 수 없습니다.');
loading = false;
return;
}
 
if (!articleId) {
searchFallback(menu).catch(function () {
setRelatedMessage(menu, '관련 문서가 없습니다.');
loading = false;
});
return;
return;
}
}


new mw.Api().get({
left = content.getBoundingClientRect().left + window.scrollX;
action: 'query',
document.documentElement.style.setProperty('--pvz-content-left', left + 'px');
prop: 'links',
pageids: articleId,
plnamespace: 0,
pllimit: 20,
formatversion: 2
}).then(function (data) {
var titles = extractLinkedTitles(data);
 
if (titles.length) {
renderRelatedPages(menu, titles);
return;
}
 
return searchFallback(menu);
}).catch(function () {
return searchFallback(menu);
}).catch(function () {
setRelatedMessage(menu, '관련 문서를 불러올 수 없습니다.');
loading = false;
});
}
}


function installV25Interface() {
function installV26Interface() {
var panel = document.querySelector('.skin-vector-legacy #mw-panel');
var panel = document.querySelector('.skin-vector-legacy #mw-panel');


clearV24Interface();
removeRelatedInterface();


if (panel) {
if (panel) {
554번째 줄: 319번째 줄:
}
}


makeRelatedInterface();
syncPanelTop();
syncPanelTop();
syncSearchTop();
syncSearchLeft();
}
}


function initV25Interface() {
function initV26Interface() {
installV25Interface();
installV26Interface();


window.addEventListener('resize', function () {
window.addEventListener('resize', function () {
syncPanelTop();
syncPanelTop();
syncSearchTop();
syncSearchLeft();
}, { passive: true });
}, { passive: true });


window.addEventListener('orientationchange', function () {
window.addEventListener('orientationchange', function () {
syncPanelTop();
syncPanelTop();
syncSearchTop();
syncSearchLeft();
}, { passive: true });
}, { passive: true });
}
}


if (document.readyState === 'loading') {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initV25Interface, { once: true });
document.addEventListener('DOMContentLoaded', initV26Interface, { once: true });
} else {
} else {
initV25Interface();
initV26Interface();
}
}
}());
}());

2026년 7월 29일 (수) 23:45 판

(function () {
	'use strict';

	var SPIN_CLASS = 'pvz-watch-spinning';
	var WATCHED_CLASS = 'pvz-watch-watched';
	var NOT_WATCHED_CLASS = 'pvz-watch-not-watched';
	var SPIN_TIME = 360;
	var cleanupTimer = 0;

	function getContainer() {
		return document.querySelector(
			'#ca-watch, #ca-unwatch, li.mw-watchlink, a.mw-watchlink'
		);
	}

	function getLink(container) {
		if (!container) {
			return null;
		}

		return container.matches('a') ? container : container.querySelector('a');
	}

	function applyState(isWatched) {
		var container = getContainer();

		if (!container) {
			return;
		}

		container.classList.toggle(WATCHED_CLASS, !!isWatched);
		container.classList.toggle(NOT_WATCHED_CLASS, !isWatched);
	}

	function syncFromMarkup() {
		var container = getContainer();
		var link = getLink(container);
		var href;

		if (!container || !link) {
			return;
		}

		href = link.getAttribute('href') || '';

		applyState(
			container.id === 'ca-unwatch' ||
			href.indexOf('action=unwatch') !== -1
		);
	}

	function spin() {
		var container = getContainer();

		if (!container) {
			return;
		}

		window.clearTimeout(cleanupTimer);
		container.classList.remove(SPIN_CLASS);
		void container.offsetWidth;
		container.classList.add(SPIN_CLASS);

		cleanupTimer = window.setTimeout(function () {
			container.classList.remove(SPIN_CLASS);
		}, SPIN_TIME + 40);
	}

	function handleClick(event) {
		var link = event.target.closest(
			'#ca-watch > a, #ca-unwatch > a, li.mw-watchlink > a, a.mw-watchlink'
		);

		if (link) {
			spin();
		}
	}

	function shortenHistoryLabel() {
		var historyLink = document.querySelector('#ca-history > a');

		if (!historyLink) {
			return;
		}

		Array.prototype.forEach.call(historyLink.childNodes, function (node) {
			if (node.nodeType === Node.TEXT_NODE) {
				node.nodeValue = node.nodeValue.replace(/역사 보기/g, '역사');
			}
		});

		if (historyLink.textContent.trim() === '역사 보기') {
			historyLink.textContent = '역사';
		}

		historyLink.setAttribute('title', '역사');
	}

	function init() {
		var observer;

		shortenHistoryLabel();
		syncFromMarkup();
		document.addEventListener('click', handleClick, true);

		observer = new MutationObserver(function () {
			shortenHistoryLabel();
			syncFromMarkup();
		});

		observer.observe(document.body, {
			subtree: true,
			childList: true,
			attributes: true,
			attributeFilter: ['id', 'href']
		});

		if (window.mw && mw.hook) {
			mw.hook('wikipage.watchlistChange').add(function (isWatched) {
				applyState(isWatched);
				spin();
			});
		}
	}

	if (document.readyState === 'loading') {
		document.addEventListener('DOMContentLoaded', init, { once: true });
	} else {
		init();
	}
}());


(function () {
	'use strict';

	function installSearchButtonIcon(wrapper) {
		var simpleSearch;
		var searchButton;
		var icon;

		if (!wrapper) {
			return;
		}

		simpleSearch = wrapper.querySelector('#simpleSearch');

		if (!simpleSearch) {
			return;
		}

		searchButton =
			simpleSearch.querySelector('#searchButton') ||
			simpleSearch.querySelector('.searchButton:not(#mw-searchButton)');

		if (!searchButton) {
			return;
		}

		searchButton.classList.add('pvz-primary-search-button');

		icon = simpleSearch.querySelector('.pvz-search-button-icon');

		if (!icon) {
			icon = document.createElement('span');
			icon.className = 'pvz-search-button-icon';
			icon.setAttribute('aria-hidden', 'true');
			simpleSearch.appendChild(icon);
		}
	}

	function installTopSearch() {
		var content = document.querySelector('.skin-vector-legacy #content');
		var form = document.querySelector('#searchform');
		var wrapper;

		if (!content || !form || !document.body) {
			return;
		}

		wrapper = document.getElementById('pvz-content-search');

		if (!wrapper) {
			wrapper = document.createElement('div');
			wrapper.id = 'pvz-content-search';
			wrapper.setAttribute('role', 'search');
			wrapper.setAttribute('aria-label', '사이트 검색');
		}

		if (wrapper.parentNode !== document.body) {
			document.body.insertBefore(wrapper, content);
		}

		if (form.parentNode !== wrapper) {
			wrapper.appendChild(form);
		}

		installSearchButtonIcon(wrapper);
		document.documentElement.classList.add('pvz-content-search-ready');
	}

	function initTopSearch() {
		var observer;

		installTopSearch();

		observer = new MutationObserver(function () {
			installTopSearch();
		});

		observer.observe(document.body, {
			childList: true,
			subtree: true
		});
	}

	if (document.readyState === 'loading') {
		document.addEventListener('DOMContentLoaded', initTopSearch, { once: true });
	} else {
		initTopSearch();
	}
}());


(function () {
	'use strict';

	function syncSearchLeft() {
		var content = document.querySelector('.skin-vector-legacy #content');
		var left;

		if (!content) {
			return;
		}

		left = content.getBoundingClientRect().left + window.scrollX;
		document.documentElement.style.setProperty('--pvz-content-left', left + 'px');
	}

	function initSearchAlignment() {
		syncSearchLeft();
		window.addEventListener('resize', syncSearchLeft, { passive: true });
		window.addEventListener('orientationchange', syncSearchLeft, { passive: true });
	}

	if (document.readyState === 'loading') {
		document.addEventListener('DOMContentLoaded', initSearchAlignment, { once: true });
	} else {
		initSearchAlignment();
	}
}());


(function () {
	'use strict';

	function removeRelatedInterface() {
		var ids = ['pvz-related-toggle', 'pvz-related-menu'];

		ids.forEach(function (id) {
			var element = document.getElementById(id);

			if (element) {
				element.remove();
			}
		});
	}

	function moveLogoOutsidePanel(panel) {
		var logo = panel && panel.querySelector('#p-logo');
		var wrapper = document.getElementById('pvz-sidebar-logo');

		if (!logo || !document.body) {
			return;
		}

		if (!wrapper) {
			wrapper = document.createElement('div');
			wrapper.id = 'pvz-sidebar-logo';
			document.body.appendChild(wrapper);
		}

		if (logo.parentNode !== wrapper) {
			wrapper.appendChild(logo);
		}
	}

	function syncPanelTop() {
		var content = document.querySelector('.skin-vector-legacy #content');
		var top;

		if (!content) {
			return;
		}

		top = content.getBoundingClientRect().top + window.scrollY;
		document.documentElement.style.setProperty('--pvz-content-top', top + 'px');
	}

	function syncSearchLeft() {
		var content = document.querySelector('.skin-vector-legacy #content');
		var left;

		if (!content) {
			return;
		}

		left = content.getBoundingClientRect().left + window.scrollX;
		document.documentElement.style.setProperty('--pvz-content-left', left + 'px');
	}

	function installV26Interface() {
		var panel = document.querySelector('.skin-vector-legacy #mw-panel');

		removeRelatedInterface();

		if (panel) {
			moveLogoOutsidePanel(panel);
		}

		syncPanelTop();
		syncSearchLeft();
	}

	function initV26Interface() {
		installV26Interface();

		window.addEventListener('resize', function () {
			syncPanelTop();
			syncSearchLeft();
		}, { passive: true });

		window.addEventListener('orientationchange', function () {
			syncPanelTop();
			syncSearchLeft();
		}, { passive: true });
	}

	if (document.readyState === 'loading') {
		document.addEventListener('DOMContentLoaded', initV26Interface, { once: true });
	} else {
		initV26Interface();
	}
}());