/* global mw, OO */
/*
* По идее и с теоретической помощью участника:Serhio Magpie, с благодарностью.
* Создание попапов с заранее заданным содержимым при нажатии на кнопку-ссылку на странице.
* Этот скрипт создан для шаблонов Интерактивных схем метрополитена,
* но им можно пользоваться и для создания попапа в других случаях.
*/
if ($('.popup-extend1').length)
$(function () {
const change = {};
let accmode,
api,
helpmode,
helptext,
windowManager,
windowManagerHelp;
function showPage(event, links) {
const cur = $(event.currentTarget).closest('.popup-table')
.find('.imgtogglemini'),
version = mw.html.escape(cur.data('version')),
pairs = [
['<templatestyles ', '<templatestyles '],
['<span ', '<span '],
['<div ', '<div '],
['</', '</'],
[''', "'"],
['>', '>']
];
windowManager.getWindow(version).then(function() {
windowManager.openWindow(version);
})
.fail(function() {
const pagename = mw.html.escape(cur.data('pagename')),
qr = cur.data('query'),
width = window.innerWidth,
imagewidth = Math.floor(Math.max(width * 0.6,
Number(mw.html.escape(String(cur.data('width')))))),
minwidth = Number(mw.html.escape(String(cur.data('textwidth'))));
helptext = mw.html.escape(cur.data('help')) || '';
Object.keys(qr).forEach(function(item) {
qr[item] = mw.html.escape(qr[item]);
Object.values(pairs).forEach(function(value) {
qr[item] = qr[item].replaceAll(value[0], value[1]);
});
});
change[version] = imagewidth + minwidth < width ? imagewidth : false;
api.get({
action: 'parse',
contentmodel: 'wikitext',
format: 'json',
formatversion: 2,
prop: 'text',
text:
`${qr[0]}|pagename=${pagename}|language${mw.config.get('wgUserLanguage')}|width=${imagewidth + qr[1]}`
})
.done(function(data) {
const actions = [{
action: 'close',
flags: ['safe', 'close']
}, {
action: 'help',
flags: 'primary',
icon: 'infoFilled',
title: 'Справка'
}],
ProcessDialog = createPopup('myDialog', pagename, actions, data.parse.text, false,
function(pd) {
firsttime(cur, version, pd, minwidth);
},
function() {
everytime(version, width, cur.data('helppage'), links);
});
windowManager.addWindows({
[version]: new ProcessDialog({
classes: ['popup-window', `popup-${version}`],
size: 'full'
})
});
windowManager.openWindow(version);
});
});
}
function createPopup(name, title, actions, content, escapable, firstcallback = () => null, everycallback = () => null) {
const ProcessDialog = function (config) {
ProcessDialog.super.call(this, config);
};
OO.inheritClass(ProcessDialog, OO.ui.ProcessDialog);
ProcessDialog.static.name = name;
ProcessDialog.static.title = $('<div>').text(title);
ProcessDialog.static.escapable = escapable;
ProcessDialog.static.actions = actions;
ProcessDialog.prototype.initialize = function () {
ProcessDialog.super.prototype.initialize.apply(this, arguments);
this.content = new OO.ui.PanelLayout({
expanded: true,
padded: true
});
this.content.$element.append(content);
this.$body.append(this.content.$element);
firstcallback(this);
ProcessDialog.prototype.getSetupProcess = function (data) {
return ProcessDialog.super.prototype.getSetupProcess.call(this, data)
.next(everycallback, this);
};
};
ProcessDialog.prototype.getActionProcess = function (action) {
const dialog = this;
if (action === 'help')
openhelp();
else if (action === 'close')
return new OO.ui.Process(function () {
dialog.close({
action: 'close'
});
});
return ProcessDialog.super.prototype.getActionProcess.call(this, action);
};
return ProcessDialog;
}
function firsttime(cur, version, pd, minwidth) {
let proposed;
const curpopup = `.popup-version-opened-${version}`;
if (change[version]) {
waitForElm(`${curpopup} .mw-collapsible`).then(function(coll) {
let div = $(coll).parent()
.children()
.first()
.children()
.first();
if ($(div).prop("tagName") !== 'DIV')
div = div.next();
div.css({
float: 'left',
maxHeight: (proposed = (window.innerHeight - pd.getContentHeight() +
pd.getBodyHeight() - Math.floor(3.5 *
$(($(coll).closest('table')
.find('tr'))[0]).height()))),
overflowY: 'auto',
width: Number(div.css('width').match(/\d+/u)[0]) + 20
});
$(coll).css({
maxHeight: proposed,
overflowY: 'auto'
});
if (coll.clientWidth < minwidth + 6) {
$(coll).css({
clear: 'both',
maxHeight: 'none',
overflowY: 'inherit'
});
div.css({
maxHeight: 'none',
overflowY: 'inherit'
});
$(curpopup).find('.noresize')
.css('float', 'inherit');
}
if (mw.html.escape(cur.data('nohr')) === 'yes')
$($(`${curpopup} hr`)[0]).css('display', 'none');
});
}
mw.util.addCSS(`${curpopup} .mw-collapsible-toggle {display:none;}`);
}
function everytime(version, maxwidth, helppage, links) {
const curpopup = $(`.popup-version-opened-${version}`).closest('.oo-ui-processDialog-content');
let des,
popup;
curpopup.find('.oo-ui-image-invert').removeClass('oo-ui-image-invert');
$('body').trigger(`refresh-imagehighlight-${links ? 'links' : 'nolinks'}1`);
if (helppage) {
curpopup.find('.oo-ui-processDialog-actions-primary a').attr({
dataHref: `/wiki/${helppage}`
});
}
if (change[version]) {
des = curpopup.find('*');
popup = curpopup.find('.popupclass');
des.each(function(data) {
const item = $(des[data]);
if (item.css('clear') === 'both')
item.css('clear', 'inherit');
if (item.css('max-width') !== 'none')
item.css('max-width', '');
});
popup.find('hr').remove();
popup.find('.noresize').css('float', 'left');
popup.parent().css('width', Math.floor(Math.max(maxwidth * 0.96, change[version])));
}
}
function waitForElm(selector) {
return new Promise(function(resolve) {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(function() {
if (document.querySelector(selector)) {
resolve(document.querySelector(selector));
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
function createcallback(value) {
return function() {
const cur = $(this);
cur.html($('<a>').html(cur.html()));
cur.attr({
role: 'button',
tabindex: '0'
});
cur.click(function(event) {
if (!windowManager) {
windowManager = new OO.ui.WindowManager();
$(document.body).append(windowManager.$element);
}
showPage(event, value);
});
cur.on('keydown', function (event) {
if ((event.which === 13 || event.which === 32) && ! (event.shiftKey && event.ctrlKey && event.altKey)) {
cur.click();
event.preventDefault();
}
});
};
}
function openwindow(version) {
const premise = windowManagerHelp.openWindow(version);
premise.opened.then(function () {
const link = $('.helpscreen .oo-ui-processDialog-actions-primary a');
link.removeAttr('role')
.off('click keydown keypress mousedown');
link.attr({
href: $('.popup-window:visible .oo-ui-processDialog-actions-primary a').attr('dataHref'),
target: '_popuphelp'
});
});
premise.closed.then(function () {
helpmode = false;
});
}
function openhelp() {
const version = 'allthehelp',
messagetext = [
'<br/><h3>Режим доступности:</h3>',
'<b>Стрелка вправо / влево</b> — перейти на следующую / предыдущую ссылку',
'<b>Стрелка вниз / вверх</b> — перейти на следующую / предыдущую линию',
'Enter — открыть выделенную ссылку',
'v — открыть выделенную ссылку в новой вкладке браузера',
'<b>Home / End</b> — перейти в начало / конец списка ссылок',
'<b>Page down / up</b> — перейти на страницу вперёд / назад',
'Shift-стрелки — перемотка схемы',
'<b>? или i</b> — эта инструкция',
'Esc — выход из режима доступности',
'Ctrl-F — как правило, запускает поиск браузера',
'(Кнопки, включающие режим, выделены жирным шрифтом)'
];
helpmode = true;
if (!windowManagerHelp) {
windowManagerHelp = new OO.ui.WindowManager();
$(document.body).append(windowManagerHelp.$element);
}
windowManagerHelp.getWindow(version).then(function () {
openwindow(version);
})
.fail(function () {
let content = helptext.replace('на эту кнопку', 'на кнопку «Подробнее»');
$.each(messagetext, function (index, elem) {
content = `${content}<br/>${elem}`;
});
const actions = [{
action: 'close',
flags: 'safe',
label: 'ОК'
}, {
action: 'morehelp',
flags: 'primary',
label: 'Подробнее'
}],
ProcessDialog = createPopup('myHelp', 'Справка об интерактивной схеме',
actions, `${content}<br/><br/>`, true);
mw.util.addCSS('.helpscreen {line-height: 22px}');
windowManagerHelp.addWindows({
[version]: new ProcessDialog({
classes: ['helpscreen'],
size: 'large'
})
});
openwindow(version);
});
}
function keyalone(event, shift) {
return $('.popup-window:visible').length > 0 && ! helpmode &&
! (event.ctrlKey || event.altKey) && (shift || ! event.shiftKey);
}
function scrollimage(dir, value, event) {
if (keyalone(event, true)) {
$('.popup-window:visible .mw-collapsible').parent()
.children()
.first()
.children()
.first()
.children()[0].scrollIntoView({
behavior: 'smooth',
[dir]: value
});
event.preventDefault();
}
}
function helpkeys() {
const links = [],
first = 0,
last = 1,
lastline = 2,
cur = 3;
let tabcounter = 0;
function startmode(position) {
const popup = $('.popup-window:visible');
let newlink;
links[first] = popup.find('li:first-child a');
links[last] = popup.find('li:last-child a');
links[lastline] = popup.find('li.ts-ОКИ-group').last()
.find('a');
newlink = links[position];
if (links[cur] && links[cur].is(':visible'))
newlink = links[cur];
else
links[cur] = undefined;
popup.find('li a')
.on('focus', function () {
$(this).closest('li')
.trigger('mouseover');
})
.on('blur', function () {
$(this).closest('li')
.trigger('mouseleave');
});
popup.find('.oo-ui-window-body').css({
pointerEvents: 'none'
});
popup.find('li.liHighlighting').removeClass('liHighlighting');
accmode = true;
setTimeout(function() {
setfocus(newlink);
}, 0);
}
function nofocus() {
return ! $(document.activeElement).closest('li').length;
}
function setfocus(link) {
let newlink = link;
if (! (newlink && newlink.is(':visible'))) {
newlink = links[cur];
if (! (newlink && newlink.is(':visible'))) {
newlink = links[first];
if (! (newlink && newlink.is(':visible')))
newlink = $('.popup-window:visible').find('li:first-child a');
}
}
newlink.focus();
links[cur] = newlink;
}
$(document).on('keydown', function (event) {
switch (event.which) {
case 9:
if (keyalone(event)) { // Tab
if (! accmode)
startmode(first);
else if (nofocus())
setfocus(links[cur]);
else if (links[last].is(':focus'))
setfocus(links[first]);
else
setfocus($(document.activeElement).closest('li')
.nextAll('li:visible')
.first()
.find('a'));
if (tabcounter++ === 1)
openhelp();
event.preventDefault();
} else if (keyalone(event, true)) { // Shift-Tab
if (! accmode)
startmode(last);
else if (nofocus())
setfocus(links[cur]);
else if (links[first].is(':focus'))
setfocus(links[last]);
else
setfocus($(document.activeElement).closest('li')
.prevAll('li:visible')
.first()
.find('a'));
if (tabcounter++ === 1)
openhelp();
event.preventDefault();
}
break;
case 27: // Esc
if (keyalone(event)) {
if (! accmode)
$('.popup-window:visible .oo-ui-processDialog-actions-safe span a')[0].click();
else
$(document.activeElement).blur();
$('.popup-window:visible .oo-ui-window-body').css({
pointerEvents: 'initial'
});
accmode = false;
event.preventDefault();
}
break;
case 33: // Page up
if (keyalone(event)) {
if (! accmode)
startmode(first);
else if (nofocus())
setfocus(links[cur]);
else if (links[first].is(':focus'))
setfocus(links[lastline]);
else
setfocus($(document.activeElement).closest('li')
.prevAll('li.ts-ОКИ-area:visible')
.first()
.find('a'));
event.preventDefault();
}
break;
case 34: { // Page down
let nextarea;
if (keyalone(event)) {
if (! accmode)
startmode(first);
else if (nofocus())
setfocus(links[cur]);
else if (links[lastline].is(':focus'))
setfocus(links[first]);
else if ((nextarea = $(document.activeElement).closest('li')
.nextAll('li.ts-ОКИ-area:visible')
.first()
.find('a')).length)
setfocus(nextarea);
else
setfocus(links[first]);
event.preventDefault();
}
break;
}
case 35: // End
if (keyalone(event)) {
if (! accmode)
startmode(last);
else
setfocus(links[last]);
event.preventDefault();
}
break;
case 36: // Home
if (keyalone(event)) {
if (! accmode)
startmode(first);
else
setfocus(links[first]);
event.preventDefault();
}
break;
case 37: // <-
if (keyalone(event)) {
if (! accmode)
startmode(last);
else if (nofocus())
setfocus(links[cur]);
else if (links[first].is(':focus'))
setfocus(links[last]);
else
setfocus($(document.activeElement).closest('li')
.prevAll('li:visible')
.first()
.find('a'));
event.preventDefault();
} else
scrollimage('inline', 'start', event);
break;
case 38: // |^
if (keyalone(event)) {
if (! accmode)
startmode(lastline);
else if (nofocus())
setfocus(links[cur]);
else if (links[first].is(':focus'))
setfocus(links[lastline]);
else
setfocus($(document.activeElement).closest('li')
.prevAll('li.ts-ОКИ-group:visible')
.first()
.find('a'));
event.preventDefault();
} else
scrollimage('block', 'start', event);
break;
case 39: // ->
if (keyalone(event)) {
if (! accmode)
startmode(first);
else if (nofocus())
setfocus(links[cur]);
else if (links[last].is(':focus'))
setfocus(links[first]);
else
setfocus($(document.activeElement).closest('li')
.nextAll('li:visible')
.first()
.find('a'));
event.preventDefault();
} else
scrollimage('inline', 'end', event);
break;
case 40: { // |v
let nextline;
if (keyalone(event)) {
if (! accmode)
startmode(first);
else if (nofocus())
setfocus(links[cur]);
else if (links[lastline].is(':focus'))
setfocus(links[first]);
else if ((nextline = $(document.activeElement).closest('li')
.nextAll('li.ts-ОКИ-group:visible')
.first()
.find('a')).length)
setfocus(nextline);
else
setfocus(links[first]);
event.preventDefault();
} else
scrollimage('block', 'end', event);
break;
}
case 73: // i
case 191: // ?
if (keyalone(event, true)) {
if (! accmode)
startmode(first);
openhelp();
event.preventDefault();
}
break;
case 86: // v
if (keyalone(event, true) && accmode && ! nofocus()) {
window.open($(document.activeElement).prop('href'), '_blank').focus();
event.preventDefault();
}
break;
default:
}
});
}
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'oojs', 'oojs-ui', 'oojs-ui-core', 'oojs-ui-windows'])
.then(function () {
if (!$('.skin-minerva').length)
$('.popup-placeholder').remove();
api = new mw.Api();
$('.metrominiicon').closest('a')
.attr('target', '_popuphelp');
if ($('.popup-extend-links').length) {
$('.popup-extend-links').each(createcallback(true));
$('.popup-extend-nolinks').each(createcallback(false));
} else
$('.popup-extend-nolinks').each(createcallback(true));
helpkeys();
$('.imgtogglemini li a').attr('tabindex', '-1');
$('.imgtogglemini map area').each(function () {
let thistitle = $(this).attr('title');
if (thistitle === $(this).prev()
.attr('title')
|| $('.ts-ОКИ li .mw-selflink').filter(function () {
return $(this).text() === thistitle;
}).length)
$(this).attr('tabindex', '-1');
else $(this).on ('focus', function () {
mw.notify($(this).attr('title'),
{
autoHideSeconds: 5,
tag: 'accessinfo',
type: 'success'
});
});
});
});
});