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

식물 vs 좀비 위키
편집 요약 없음
태그: 되돌려진 기여
편집 요약 없음
태그: 되돌려진 기여
255번째 줄: 255번째 줄:
'use strict';
'use strict';


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


function readPreference(key, allowed, fallback) {
function clearV24Interface() {
var value;
var ids = [
'pvz-right-panel',
'pvz-left-panel-toggle',
'pvz-right-panel-toggle'
];


try {
ids.forEach(function (id) {
value = window.localStorage.getItem(key);
var element = document.getElementById(id);
} catch (error) {
value = null;
}


return allowed.indexOf(value) !== -1 ? value : fallback;
if (element) {
}
element.remove();
 
}
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);
document.documentElement.classList.remove(
}
'pvz-font-small',
 
'pvz-font-normal',
function applyFontSize(value, shouldSave) {
'pvz-font-large',
replacePreferenceClass('pvz-font-', FONT_VALUES, value);
'pvz-width-narrow',
 
'pvz-width-normal',
if (shouldSave) {
'pvz-width-wide',
savePreference(FONT_KEY, value);
'pvz-left-panel-open',
}
'pvz-right-panel-open'
 
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
);
);
}
}
350번째 줄: 305번째 줄:
}
}


function makeOption(label, group, value) {
function syncPanelTop() {
var button = document.createElement('button');
var content = document.querySelector('.skin-vector-legacy #content');
var top;


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


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


function makeSettingsSection(title, group, options) {
function syncSearchTop() {
var section = document.createElement('section');
var personal = document.querySelector('.skin-vector-legacy #p-personal');
var heading = document.createElement('h3');
var top;
var optionList = document.createElement('div');


section.className = 'pvz-settings-section';
if (!personal) {
heading.className = 'pvz-settings-heading';
return;
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;
top = personal.getBoundingClientRect().bottom + window.scrollY + 5;
document.documentElement.style.setProperty('--pvz-search-top', top + 'px');
}
}


function buildRightPanel() {
function makeRelatedInterface() {
var rightPanel = document.getElementById('pvz-right-panel');
var button = document.getElementById('pvz-related-toggle');
var menu = document.getElementById('pvz-related-menu');
var lines;
var title;
var list;


if (!document.body) {
if (!document.body) {
394번째 줄: 340번째 줄:
}
}


if (!rightPanel) {
if (!button) {
rightPanel = document.createElement('aside');
button = document.createElement('button');
rightPanel.id = 'pvz-right-panel';
button.id = 'pvz-related-toggle';
rightPanel.setAttribute('aria-label', '화면 설정');
button.type = 'button';
document.body.appendChild(rightPanel);
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);
}
}


rightPanel.textContent = '';
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';


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


rightPanel.appendChild(
if (button.getAttribute('data-pvz-ready') !== 'true') {
makeSettingsSection('화면 너비', 'width', [
button.setAttribute('data-pvz-ready', 'true');
{ label: '좁게', value: 'narrow' },
{ label: '보통', value: 'normal' },
{ label: '넓게', value: 'wide' }
])
);


rightPanel.addEventListener('click', function (event) {
button.addEventListener('click', function () {
var option = event.target.closest('.pvz-settings-option');
var willOpen = !menu.classList.contains('is-open');
var group;
var value;


if (!option) {
menu.classList.toggle('is-open', willOpen);
return;
button.setAttribute('aria-expanded', willOpen ? 'true' : 'false');
}
button.setAttribute(
'aria-label',
willOpen ? '관련 문서 닫기' : '관련 문서 열기'
);


group = option.getAttribute('data-pvz-setting-group');
if (willOpen && !loaded && !loading) {
value = option.getAttribute('data-pvz-setting-value');
loadRelatedPages(menu);
}
});


if (group === 'font' && FONT_VALUES.indexOf(value) !== -1) {
document.addEventListener('click', function (event) {
applyFontSize(value, true);
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', '관련 문서 열기');
}
});
}
}


if (group === 'width' && WIDTH_VALUES.indexOf(value) !== -1) {
function setRelatedMessage(menu, message) {
applyContentWidth(value, true);
var list = menu.querySelector('.pvz-related-list');
}
var item = document.createElement('li');
});
var text = document.createElement('span');


updateControlStates();
list.textContent = '';
text.className = 'pvz-related-empty';
text.textContent = message;
item.appendChild(text);
list.appendChild(item);
}
}


function makePanelToggle(id, label, side) {
function renderRelatedPages(menu, titles) {
var button = document.getElementById(id);
var list = menu.querySelector('.pvz-related-list');
var lines;
 
list.textContent = '';


if (!button) {
titles.slice(0, MAX_RELATED).forEach(function (title) {
button = document.createElement('button');
var item = document.createElement('li');
button.id = id;
var link = document.createElement('a');
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');
link.href = mw.util.getUrl(title);
lines.className = 'pvz-panel-toggle-lines';
link.textContent = title;
lines.setAttribute('aria-hidden', 'true');
item.appendChild(link);
button.appendChild(lines);
list.appendChild(item);
});


document.body.appendChild(button);
if (!list.children.length) {
setRelatedMessage(menu, '관련 문서가 없습니다.');
}
}


return button;
loaded = true;
loading = false;
}
}


function installPanelToggles() {
function extractLinkedTitles(data) {
var leftToggle = makePanelToggle(
var pages = data && data.query && data.query.pages;
'pvz-left-panel-toggle',
var currentTitle = mw.config.get('wgPageName').replace(/_/g, ' ');
'왼쪽 보조창 열기',
var titles = [];
'left'
var seen = Object.create(null);
);
var rightToggle = makePanelToggle(
'pvz-right-panel-toggle',
'오른쪽 설정창 열기',
'right'
);


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


button.setAttribute('data-pvz-toggle-ready', 'true');
Object.keys(pages).some(function (pageId) {
var links = pages[pageId].links || [];


button.addEventListener('click', function () {
links.forEach(function (link) {
var root = document.documentElement;
var title = link.title;
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);
if (
root.classList.toggle(targetClass, willOpen);
title &&
updateToggleStates();
title !== currentTitle &&
!seen[title] &&
titles.length < MAX_RELATED
) {
seen[title] = true;
titles.push(title);
}
});
});
return titles.length >= MAX_RELATED;
});
});


updateToggleStates();
return titles;
}
}


function updateControlStates() {
function searchFallback(menu) {
var root = document.documentElement;
var pageTitle = mw.config.get('wgTitle') || '';
var options = document.querySelectorAll('.pvz-settings-option');


Array.prototype.forEach.call(options, function (option) {
return new mw.Api().get({
var group = option.getAttribute('data-pvz-setting-group');
action: 'query',
var value = option.getAttribute('data-pvz-setting-value');
list: 'search',
var active =
srsearch: pageTitle,
(group === 'font' && root.classList.contains('pvz-font-' + value)) ||
srnamespace: 0,
(group === 'width' && root.classList.contains('pvz-width-' + value));
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);


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


function updateToggleStates() {
function loadRelatedPages(menu) {
var root = document.documentElement;
var articleId = mw.config.get('wgArticleId');
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) {
loading = true;
leftToggle.setAttribute('aria-expanded', leftOpen ? 'true' : 'false');
setRelatedMessage(menu, '관련 문서를 불러오는 중…');
leftToggle.setAttribute(
'aria-label',
leftOpen ? '왼쪽 보조창 닫기' : '왼쪽 보조창 열기'
);
leftToggle.setAttribute(
'title',
leftOpen ? '왼쪽 보조창 닫기' : '왼쪽 보조창 열기'
);
}


if (rightToggle) {
if (!window.mw || !mw.Api || !mw.util) {
rightToggle.setAttribute('aria-expanded', rightOpen ? 'true' : 'false');
setRelatedMessage(menu, '관련 문서를 불러올 수 없습니다.');
rightToggle.setAttribute(
loading = false;
'aria-label',
return;
rightOpen ? '오른쪽 설정창 닫기' : '오른쪽 설정창 열기'
);
rightToggle.setAttribute(
'title',
rightOpen ? '오른쪽 설정창 닫기' : '오른쪽 설정창 열기'
);
}
}
}


function syncPanelTop() {
if (!articleId) {
var content = document.querySelector('.skin-vector-legacy #content');
searchFallback(menu).catch(function () {
var top;
setRelatedMessage(menu, '관련 문서가 없습니다.');
 
loading = false;
if (!content) {
});
return;
return;
}
}


top = content.getBoundingClientRect().top + window.scrollY;
new mw.Api().get({
document.documentElement.style.setProperty('--pvz-content-top', top + 'px');
action: 'query',
}
prop: 'links',
 
pageids: articleId,
function syncSearchLeft() {
plnamespace: 0,
var content = document.querySelector('.skin-vector-legacy #content');
pllimit: 20,
var left;
formatversion: 2
}).then(function (data) {
var titles = extractLinkedTitles(data);


if (!content) {
if (titles.length) {
return;
renderRelatedPages(menu, titles);
}
return;
}


left = content.getBoundingClientRect().left + window.scrollX;
return searchFallback(menu);
document.documentElement.style.setProperty('--pvz-content-left', left + 'px');
}).catch(function () {
return searchFallback(menu);
}).catch(function () {
setRelatedMessage(menu, '관련 문서를 불러올 수 없습니다.');
loading = false;
});
}
}


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


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


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


function initSidePanels() {
function initV25Interface() {
installSidePanels();
installV25Interface();


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


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


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