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

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


function removeIds(root) {
var FONT_VALUES = ['small', 'normal', 'large'];
var nodes;
var WIDTH_VALUES = ['narrow', 'normal', 'wide'];
var FONT_KEY = 'pvz-vector-font-size';
var WIDTH_KEY = 'pvz-vector-content-width';


if (!root) {
function readPreference(key, allowed, fallback) {
return;
var value;
 
try {
value = window.localStorage.getItem(key);
} catch (error) {
value = null;
}
}


if (root.hasAttribute && root.hasAttribute('id')) {
return allowed.indexOf(value) !== -1 ? value : fallback;
root.removeAttribute('id');
}
 
function savePreference(key, value) {
try {
window.localStorage.setItem(key, value);
} catch (error) {
/* 저장소를 사용할 수 없는 환경에서는 현재 화면에만 적용한다. */
}
}
}


nodes = root.querySelectorAll('[id], [aria-labelledby], [aria-controls]');
function replacePreferenceClass(prefix, allowed, value) {
var root = document.documentElement;


Array.prototype.forEach.call(nodes, function (node) {
allowed.forEach(function (item) {
node.removeAttribute('id');
root.classList.remove(prefix + item);
node.removeAttribute('aria-labelledby');
node.removeAttribute('aria-controls');
});
});
root.classList.add(prefix + value);
}
function applyFontSize(value, shouldSave) {
replacePreferenceClass('pvz-font-', FONT_VALUES, value);
if (shouldSave) {
savePreference(FONT_KEY, value);
}
updateControlStates();
}
function applyContentWidth(value, shouldSave) {
replacePreferenceClass('pvz-width-', WIDTH_VALUES, value);
if (value !== 'wide') {
document.documentElement.classList.remove(
'pvz-left-panel-open',
'pvz-right-panel-open'
);
}
if (shouldSave) {
savePreference(WIDTH_KEY, value);
}
updateControlStates();
updateToggleStates();
window.requestAnimationFrame(syncPanelTop);
window.requestAnimationFrame(syncSearchLeft);
}
function applyStoredPreferences() {
applyFontSize(
readPreference(FONT_KEY, FONT_VALUES, 'normal'),
false
);
applyContentWidth(
readPreference(WIDTH_KEY, WIDTH_VALUES, 'normal'),
false
);
}
}


294번째 줄: 350번째 줄:
}
}


function buildRightPanel(panel) {
function makeOption(label, group, value) {
var button = document.createElement('button');
 
button.type = 'button';
button.className = 'pvz-settings-option';
button.textContent = label;
button.setAttribute('data-pvz-setting-group', group);
button.setAttribute('data-pvz-setting-value', value);
button.setAttribute('aria-pressed', 'false');
 
return button;
}
 
function makeSettingsSection(title, group, options) {
var section = document.createElement('section');
var heading = document.createElement('h3');
var optionList = document.createElement('div');
 
section.className = 'pvz-settings-section';
heading.className = 'pvz-settings-heading';
heading.textContent = title;
optionList.className = 'pvz-settings-options';
optionList.setAttribute('role', 'group');
optionList.setAttribute('aria-label', title);
 
options.forEach(function (option) {
optionList.appendChild(
makeOption(option.label, group, option.value)
);
});
 
section.appendChild(heading);
section.appendChild(optionList);
 
return section;
}
 
function buildRightPanel() {
var rightPanel = document.getElementById('pvz-right-panel');
var rightPanel = document.getElementById('pvz-right-panel');
var portals;


if (!panel || !document.body) {
if (!document.body) {
return;
return;
}
}
305번째 줄: 397번째 줄:
rightPanel = document.createElement('aside');
rightPanel = document.createElement('aside');
rightPanel.id = 'pvz-right-panel';
rightPanel.id = 'pvz-right-panel';
rightPanel.setAttribute('aria-label', '오른쪽 보조 메뉴');
rightPanel.setAttribute('aria-label', '화면 설정');
document.body.appendChild(rightPanel);
document.body.appendChild(rightPanel);
}
}


rightPanel.textContent = '';
rightPanel.textContent = '';
portals = panel.querySelectorAll('.vector-menu-portal, .portal');


Array.prototype.forEach.call(portals, function (portal) {
rightPanel.appendChild(
var clone = portal.cloneNode(true);
makeSettingsSection('글자 크기', 'font', [
removeIds(clone);
{ label: '작게', value: 'small' },
rightPanel.appendChild(clone);
{ label: '보통', value: 'normal' },
{ label: '크게', value: 'large' }
])
);
 
rightPanel.appendChild(
makeSettingsSection('화면 너비', 'width', [
{ label: '좁게', value: 'narrow' },
{ label: '보통', value: 'normal' },
{ label: '넓게', value: 'wide' }
])
);
 
rightPanel.addEventListener('click', function (event) {
var option = event.target.closest('.pvz-settings-option');
var group;
var value;
 
if (!option) {
return;
}
 
group = option.getAttribute('data-pvz-setting-group');
value = option.getAttribute('data-pvz-setting-value');
 
if (group === 'font' && FONT_VALUES.indexOf(value) !== -1) {
applyFontSize(value, true);
}
 
if (group === 'width' && WIDTH_VALUES.indexOf(value) !== -1) {
applyContentWidth(value, true);
}
});
});
updateControlStates();
}
function makePanelToggle(id, label, side) {
var button = document.getElementById(id);
var lines;
if (!button) {
button = document.createElement('button');
button.id = id;
button.type = 'button';
button.className = 'pvz-panel-toggle';
button.setAttribute('aria-label', label);
button.setAttribute('title', label);
button.setAttribute('aria-expanded', 'false');
button.setAttribute('data-pvz-panel-side', side);
lines = document.createElement('span');
lines.className = 'pvz-panel-toggle-lines';
lines.setAttribute('aria-hidden', 'true');
button.appendChild(lines);
document.body.appendChild(button);
}
return button;
}
function installPanelToggles() {
var leftToggle = makePanelToggle(
'pvz-left-panel-toggle',
'왼쪽 보조창 열기',
'left'
);
var rightToggle = makePanelToggle(
'pvz-right-panel-toggle',
'오른쪽 설정창 열기',
'right'
);
[leftToggle, rightToggle].forEach(function (button) {
if (button.getAttribute('data-pvz-toggle-ready') === 'true') {
return;
}
button.setAttribute('data-pvz-toggle-ready', 'true');
button.addEventListener('click', function () {
var root = document.documentElement;
var side = button.getAttribute('data-pvz-panel-side');
var targetClass =
side === 'left'
? 'pvz-left-panel-open'
: 'pvz-right-panel-open';
var otherClass =
side === 'left'
? 'pvz-right-panel-open'
: 'pvz-left-panel-open';
var willOpen = !root.classList.contains(targetClass);
root.classList.remove(otherClass);
root.classList.toggle(targetClass, willOpen);
updateToggleStates();
});
});
updateToggleStates();
}
function updateControlStates() {
var root = document.documentElement;
var options = document.querySelectorAll('.pvz-settings-option');
Array.prototype.forEach.call(options, function (option) {
var group = option.getAttribute('data-pvz-setting-group');
var value = option.getAttribute('data-pvz-setting-value');
var active =
(group === 'font' && root.classList.contains('pvz-font-' + value)) ||
(group === 'width' && root.classList.contains('pvz-width-' + value));
option.classList.toggle('is-active', active);
option.setAttribute('aria-pressed', active ? 'true' : 'false');
});
}
function updateToggleStates() {
var root = document.documentElement;
var leftToggle = document.getElementById('pvz-left-panel-toggle');
var rightToggle = document.getElementById('pvz-right-panel-toggle');
var leftOpen = root.classList.contains('pvz-left-panel-open');
var rightOpen = root.classList.contains('pvz-right-panel-open');
if (leftToggle) {
leftToggle.setAttribute('aria-expanded', leftOpen ? 'true' : 'false');
leftToggle.setAttribute(
'aria-label',
leftOpen ? '왼쪽 보조창 닫기' : '왼쪽 보조창 열기'
);
leftToggle.setAttribute(
'title',
leftOpen ? '왼쪽 보조창 닫기' : '왼쪽 보조창 열기'
);
}
if (rightToggle) {
rightToggle.setAttribute('aria-expanded', rightOpen ? 'true' : 'false');
rightToggle.setAttribute(
'aria-label',
rightOpen ? '오른쪽 설정창 닫기' : '오른쪽 설정창 열기'
);
rightToggle.setAttribute(
'title',
rightOpen ? '오른쪽 설정창 닫기' : '오른쪽 설정창 열기'
);
}
}
}


329번째 줄: 567번째 줄:
top = content.getBoundingClientRect().top + window.scrollY;
top = content.getBoundingClientRect().top + window.scrollY;
document.documentElement.style.setProperty('--pvz-content-top', top + 'px');
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');
}
}


334번째 줄: 584번째 줄:
var panel = document.querySelector('.skin-vector-legacy #mw-panel');
var panel = document.querySelector('.skin-vector-legacy #mw-panel');


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


moveLogoOutsidePanel(panel);
moveLogoOutsidePanel(panel);
buildRightPanel(panel);
buildRightPanel();
installPanelToggles();
syncPanelTop();
syncPanelTop();
syncSearchLeft();
}
}


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


if (document.readyState === 'loading') {
if (document.readyState === 'loading') {

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

(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';

	var FONT_VALUES = ['small', 'normal', 'large'];
	var WIDTH_VALUES = ['narrow', 'normal', 'wide'];
	var FONT_KEY = 'pvz-vector-font-size';
	var WIDTH_KEY = 'pvz-vector-content-width';

	function readPreference(key, allowed, fallback) {
		var value;

		try {
			value = window.localStorage.getItem(key);
		} catch (error) {
			value = null;
		}

		return allowed.indexOf(value) !== -1 ? value : fallback;
	}

	function savePreference(key, value) {
		try {
			window.localStorage.setItem(key, value);
		} catch (error) {
			/* 저장소를 사용할 수 없는 환경에서는 현재 화면에만 적용한다. */
		}
	}

	function replacePreferenceClass(prefix, allowed, value) {
		var root = document.documentElement;

		allowed.forEach(function (item) {
			root.classList.remove(prefix + item);
		});

		root.classList.add(prefix + value);
	}

	function applyFontSize(value, shouldSave) {
		replacePreferenceClass('pvz-font-', FONT_VALUES, value);

		if (shouldSave) {
			savePreference(FONT_KEY, value);
		}

		updateControlStates();
	}

	function applyContentWidth(value, shouldSave) {
		replacePreferenceClass('pvz-width-', WIDTH_VALUES, value);

		if (value !== 'wide') {
			document.documentElement.classList.remove(
				'pvz-left-panel-open',
				'pvz-right-panel-open'
			);
		}

		if (shouldSave) {
			savePreference(WIDTH_KEY, value);
		}

		updateControlStates();
		updateToggleStates();
		window.requestAnimationFrame(syncPanelTop);
		window.requestAnimationFrame(syncSearchLeft);
	}

	function applyStoredPreferences() {
		applyFontSize(
			readPreference(FONT_KEY, FONT_VALUES, 'normal'),
			false
		);
		applyContentWidth(
			readPreference(WIDTH_KEY, WIDTH_VALUES, 'normal'),
			false
		);
	}

	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 makeOption(label, group, value) {
		var button = document.createElement('button');

		button.type = 'button';
		button.className = 'pvz-settings-option';
		button.textContent = label;
		button.setAttribute('data-pvz-setting-group', group);
		button.setAttribute('data-pvz-setting-value', value);
		button.setAttribute('aria-pressed', 'false');

		return button;
	}

	function makeSettingsSection(title, group, options) {
		var section = document.createElement('section');
		var heading = document.createElement('h3');
		var optionList = document.createElement('div');

		section.className = 'pvz-settings-section';
		heading.className = 'pvz-settings-heading';
		heading.textContent = title;
		optionList.className = 'pvz-settings-options';
		optionList.setAttribute('role', 'group');
		optionList.setAttribute('aria-label', title);

		options.forEach(function (option) {
			optionList.appendChild(
				makeOption(option.label, group, option.value)
			);
		});

		section.appendChild(heading);
		section.appendChild(optionList);

		return section;
	}

	function buildRightPanel() {
		var rightPanel = document.getElementById('pvz-right-panel');

		if (!document.body) {
			return;
		}

		if (!rightPanel) {
			rightPanel = document.createElement('aside');
			rightPanel.id = 'pvz-right-panel';
			rightPanel.setAttribute('aria-label', '화면 설정');
			document.body.appendChild(rightPanel);
		}

		rightPanel.textContent = '';

		rightPanel.appendChild(
			makeSettingsSection('글자 크기', 'font', [
				{ label: '작게', value: 'small' },
				{ label: '보통', value: 'normal' },
				{ label: '크게', value: 'large' }
			])
		);

		rightPanel.appendChild(
			makeSettingsSection('화면 너비', 'width', [
				{ label: '좁게', value: 'narrow' },
				{ label: '보통', value: 'normal' },
				{ label: '넓게', value: 'wide' }
			])
		);

		rightPanel.addEventListener('click', function (event) {
			var option = event.target.closest('.pvz-settings-option');
			var group;
			var value;

			if (!option) {
				return;
			}

			group = option.getAttribute('data-pvz-setting-group');
			value = option.getAttribute('data-pvz-setting-value');

			if (group === 'font' && FONT_VALUES.indexOf(value) !== -1) {
				applyFontSize(value, true);
			}

			if (group === 'width' && WIDTH_VALUES.indexOf(value) !== -1) {
				applyContentWidth(value, true);
			}
		});

		updateControlStates();
	}

	function makePanelToggle(id, label, side) {
		var button = document.getElementById(id);
		var lines;

		if (!button) {
			button = document.createElement('button');
			button.id = id;
			button.type = 'button';
			button.className = 'pvz-panel-toggle';
			button.setAttribute('aria-label', label);
			button.setAttribute('title', label);
			button.setAttribute('aria-expanded', 'false');
			button.setAttribute('data-pvz-panel-side', side);

			lines = document.createElement('span');
			lines.className = 'pvz-panel-toggle-lines';
			lines.setAttribute('aria-hidden', 'true');
			button.appendChild(lines);

			document.body.appendChild(button);
		}

		return button;
	}

	function installPanelToggles() {
		var leftToggle = makePanelToggle(
			'pvz-left-panel-toggle',
			'왼쪽 보조창 열기',
			'left'
		);
		var rightToggle = makePanelToggle(
			'pvz-right-panel-toggle',
			'오른쪽 설정창 열기',
			'right'
		);

		[leftToggle, rightToggle].forEach(function (button) {
			if (button.getAttribute('data-pvz-toggle-ready') === 'true') {
				return;
			}

			button.setAttribute('data-pvz-toggle-ready', 'true');

			button.addEventListener('click', function () {
				var root = document.documentElement;
				var side = button.getAttribute('data-pvz-panel-side');
				var targetClass =
					side === 'left'
						? 'pvz-left-panel-open'
						: 'pvz-right-panel-open';
				var otherClass =
					side === 'left'
						? 'pvz-right-panel-open'
						: 'pvz-left-panel-open';
				var willOpen = !root.classList.contains(targetClass);

				root.classList.remove(otherClass);
				root.classList.toggle(targetClass, willOpen);
				updateToggleStates();
			});
		});

		updateToggleStates();
	}

	function updateControlStates() {
		var root = document.documentElement;
		var options = document.querySelectorAll('.pvz-settings-option');

		Array.prototype.forEach.call(options, function (option) {
			var group = option.getAttribute('data-pvz-setting-group');
			var value = option.getAttribute('data-pvz-setting-value');
			var active =
				(group === 'font' && root.classList.contains('pvz-font-' + value)) ||
				(group === 'width' && root.classList.contains('pvz-width-' + value));

			option.classList.toggle('is-active', active);
			option.setAttribute('aria-pressed', active ? 'true' : 'false');
		});
	}

	function updateToggleStates() {
		var root = document.documentElement;
		var leftToggle = document.getElementById('pvz-left-panel-toggle');
		var rightToggle = document.getElementById('pvz-right-panel-toggle');
		var leftOpen = root.classList.contains('pvz-left-panel-open');
		var rightOpen = root.classList.contains('pvz-right-panel-open');

		if (leftToggle) {
			leftToggle.setAttribute('aria-expanded', leftOpen ? 'true' : 'false');
			leftToggle.setAttribute(
				'aria-label',
				leftOpen ? '왼쪽 보조창 닫기' : '왼쪽 보조창 열기'
			);
			leftToggle.setAttribute(
				'title',
				leftOpen ? '왼쪽 보조창 닫기' : '왼쪽 보조창 열기'
			);
		}

		if (rightToggle) {
			rightToggle.setAttribute('aria-expanded', rightOpen ? 'true' : 'false');
			rightToggle.setAttribute(
				'aria-label',
				rightOpen ? '오른쪽 설정창 닫기' : '오른쪽 설정창 열기'
			);
			rightToggle.setAttribute(
				'title',
				rightOpen ? '오른쪽 설정창 닫기' : '오른쪽 설정창 열기'
			);
		}
	}

	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 installSidePanels() {
		var panel = document.querySelector('.skin-vector-legacy #mw-panel');

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

		moveLogoOutsidePanel(panel);
		buildRightPanel();
		installPanelToggles();
		syncPanelTop();
		syncSearchLeft();
	}

	function initSidePanels() {
		installSidePanels();

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

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

	applyStoredPreferences();

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