Открыть меню
Переключить меню настроек
Открыть персональное меню
Вы не представились системе
Ваш IP-адрес будет виден всем, если вы внесёте какие-либо изменения.

MediaWiki:Common.js: различия между версиями

Страница интерфейса MediaWiki
Shorten_mobile_search_placeholder
Нет описания правки
 
(не показано 15 промежуточных версий 3 участников)
Строка 23: Строка 23:


function initParticles() {
function initParticles() {
var hero = document.querySelector('.bc-hero');
var hero = document.querySelector('.bc-hero');
if (!hero || document.getElementById('bc-particles')) {
if (!hero) {
return;
return;
}
}
 
var oldHost = document.getElementById('bc-particles');
if (oldHost) {
oldHost.remove();
}


var host = document.createElement('div');
var host = document.createElement('div');
host.id = 'bc-particles';
host.id = 'bc-particles';
hero.insertBefore(host, hero.firstChild);
hero.insertBefore(host, hero.firstChild);


loadScript('https://cdn.jsdelivr.net/npm/particles.js@2.0.0/particles.min.js', function () {
loadScript(
'https://cdn.jsdelivr.net/npm/particles.js@2.0.0/particles.min.js',
function () {
if (!window.particlesJS) {
if (!window.particlesJS) {
return;
return;
}
}
window.particlesJS('bc-particles', {
window.particlesJS('bc-particles', {
particles: {
particles: {
number: { value: 42, density: { enable: true, value_area: 900 } },
number: {
color: { value: ['#1d5f9f', '#e40046', '#2f6f73'] },
value: 110,
shape: { type: 'circle' },
density: {
opacity: { value: 0.22, random: true },
enable: true,
size: { value: 3, random: true },
value_area: 850
}
},
 
color: {
value: [
'#1d5f9f',
'#e40046',
'#33c4ff',
'#102033'
]
},
 
shape: {
type: 'circle'
},
 
opacity: {
value: 0.38,
random: true
},
 
size: {
value: 4.4,
random: true
},
 
line_linked: {
line_linked: {
enable: true,
enable: true,
distance: 150,
distance: 180,
color: '#1d5f9f',
color: '#1d5f9f',
opacity: 0.14,
opacity: 0.38,
width: 1
width: 1.5
},
},
move: {
move: {
enable: true,
enable: true,
speed: 1.1,
speed: 1.0,
direction: 'none',
direction: 'none',
random: true,
random: true,
Строка 60: Строка 95:
}
}
},
},
interactivity: {
interactivity: {
detect_on: 'canvas',
detect_on: 'canvas',
events: {
events: {
onhover: { enable: true, mode: 'grab' },
onhover: {
onclick: { enable: true, mode: 'push' },
enable: true,
mode: 'grab'
},
 
onclick: {
enable: true,
mode: 'push'
},
 
resize: true
resize: true
},
},
modes: {
modes: {
grab: { distance: 150, line_linked: { opacity: 0.26 } },
grab: {
push: { particles_nb: 3 }
distance: 190,
line_linked: {
opacity: 0.4
}
},
 
push: {
particles_nb: 4
}
}
}
},
},
retina_detect: true
retina_detect: true
});
});
});
}
}
);
}


function makeSearchInteractive() {
function makeSearchInteractive() {
Строка 89: Строка 145:
input.placeholder = window.innerWidth < 560 ? 'страна, сервис или мера...' : slot.textContent.trim();
input.placeholder = window.innerWidth < 560 ? 'страна, сервис или мера...' : slot.textContent.trim();
input.setAttribute('aria-label', 'Поиск по BRICSCompass');
input.setAttribute('aria-label', 'Поиск по BRICSCompass');
input.placeholder = window.innerWidth < 560 ? '\u0441\u0442\u0440\u0430\u043d\u0430 \u0438\u043b\u0438 \u0441\u0435\u0440\u0432\u0438\u0441' : '\u043d\u0430\u0439\u0442\u0438 \u0441\u0442\u0440\u0430\u043d\u0443, \u0441\u0435\u0440\u0432\u0438\u0441 \u0438\u043b\u0438 \u043c\u0435\u0440\u0443';
input.setAttribute('aria-label', '\u041f\u043e\u0438\u0441\u043a \u043f\u043e BRICSCompass');
input.placeholder = window.innerWidth < 560 ? '\u0421\u0442\u0440\u0430\u043d\u0430 \u0438\u043b\u0438 \u0441\u0435\u0440\u0432\u0438\u0441' : '\u0421\u0442\u0440\u0430\u043d\u0430, \u0441\u0435\u0440\u0432\u0438\u0441 \u0438\u043b\u0438 \u043c\u0435\u0440\u0430';
slot.replaceChildren(input);
slot.replaceChildren(input);
slot.classList.add('is-live');
slot.classList.add('is-live');
Строка 94: Строка 153:
var cards = Array.prototype.slice.call(document.querySelectorAll('.bc-card, .bc-country'));
var cards = Array.prototype.slice.call(document.querySelectorAll('.bc-card, .bc-country'));
var tags = Array.prototype.slice.call(document.querySelectorAll('[data-bc-query]'));
var tags = Array.prototype.slice.call(document.querySelectorAll('[data-bc-query]'));
var arrow = document.querySelector('.bc-arrow');


function filter(value) {
function filter(value) {
Строка 119: Строка 179:
});
});
});
});
function getCardLink(card) {
var link = card.closest('a');
if (link && link.href) {
return link.href;
}
return '';
}
function submitSearch() {
var query = input.value.trim().toLowerCase();
var target = null;
if (!query) {
input.focus();
return;
}
cards.some(function (card) {
var titleNode = card.querySelector('strong');
var title = (titleNode || card).textContent.trim().toLowerCase();
var text = card.textContent.trim().toLowerCase();
if (title === query) {
target = card;
return true;
}
if (!target && text.indexOf(query) !== -1) {
target = card;
}
return false;
});
var href = target && getCardLink(target);
if (href) {
window.location.href = href;
return;
}
window.location.href = (mw.config.get('wgScriptPath') || '') + '/index.php?search=' + encodeURIComponent(input.value.trim());
}
input.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
event.preventDefault();
submitSearch();
}
});
if (arrow) {
arrow.setAttribute('role', 'button');
arrow.setAttribute('tabindex', '0');
arrow.setAttribute('aria-label', 'Найти раздел');
arrow.addEventListener('click', submitSearch);
arrow.addEventListener('keydown', function (event) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
submitSearch();
}
});
}
}
}


Строка 157: Строка 273:
observer.observe(stat);
observer.observe(stat);
});
});
}
function prioritizeMedia() {
Array.prototype.forEach.call(document.querySelectorAll('.bc-photo img, .bc-section-photo img'), function (img) {
img.loading = 'eager';
img.decoding = 'async';
img.setAttribute('fetchpriority', 'high');
});
}
function removeNoopServiceWorker() {
if (!('serviceWorker' in navigator) || !navigator.serviceWorker.getRegistrations) {
return;
}
navigator.serviceWorker.getRegistrations().then(function (registrations) {
registrations.forEach(function (registration) {
var url = registration.active && registration.active.scriptURL;
if (url && url.indexOf('skins.citizen.serviceWorker') !== -1) {
registration.unregister();
}
});
}).catch(function () {});
}
function enhanceCards() {
var cards = Array.prototype.slice.call(document.querySelectorAll('.bc-card, .bc-country, .bc-stat'));
cards.forEach(function (card) {
card.setAttribute('tabindex', '0');
card.addEventListener('mousemove', function (event) {
var rect = card.getBoundingClientRect();
card.style.setProperty('--mx', Math.round(event.clientX - rect.left) + 'px');
card.style.setProperty('--my', Math.round(event.clientY - rect.top) + 'px');
});
card.addEventListener('keydown', function (event) {
var link = card.closest('a');
if (link && (event.key === 'Enter' || event.key === ' ')) {
event.preventDefault();
link.click();
}
});
});
}
function addCardFilter() {
var grid = document.querySelector('.bc-card-grid');
if (!grid || document.querySelector('.bc-hero') || document.querySelector('.bc-local-filter')) {
return;
}
var cards = Array.prototype.slice.call(grid.querySelectorAll('.bc-card'));
if (cards.length < 3) {
return;
}
var panel = document.createElement('div');
panel.className = 'bc-local-filter';
panel.innerHTML =
'<div class="bc-local-filter-title">\u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0444\u0438\u043b\u044c\u0442\u0440</div>' +
'<input type="search" class="bc-local-filter-input" placeholder="\u0424\u0438\u043b\u044c\u0442\u0440 \u043f\u043e \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435" aria-label="\u0424\u0438\u043b\u044c\u0442\u0440 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a">' +
'<div class="bc-local-filter-count"></div>';
grid.parentNode.insertBefore(panel, grid);
var input = panel.querySelector('.bc-local-filter-input');
var count = panel.querySelector('.bc-local-filter-count');
function update() {
var query = input.value.trim().toLowerCase();
var visible = 0;
cards.forEach(function (card) {
var match = !query || card.textContent.toLowerCase().indexOf(query) !== -1;
card.classList.toggle('is-hidden', !match);
if (match) {
visible += 1;
}
});
count.textContent = visible + ' / ' + cards.length;
}
input.addEventListener('input', update);
update();
}
function addReadingTools() {
if (document.querySelector('.bc-read-progress')) {
return;
}
var progress = document.createElement('div');
progress.className = 'bc-read-progress';
document.body.appendChild(progress);
var top = document.createElement('button');
top.className = 'bc-top-button';
top.type = 'button';
top.setAttribute('aria-label', '\u041d\u0430\u0432\u0435\u0440\u0445');
top.textContent = '\u2191';
document.body.appendChild(top);
top.addEventListener('click', function () {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
function update() {
var doc = document.documentElement;
var max = Math.max(1, doc.scrollHeight - window.innerHeight);
var ratio = Math.min(1, Math.max(0, window.scrollY / max));
progress.style.transform = 'scaleX(' + ratio + ')';
top.classList.toggle('is-visible', window.scrollY > 520);
}
window.addEventListener('scroll', update, { passive: true });
window.addEventListener('resize', update);
update();
}
function revealContent() {
var items = Array.prototype.slice.call(document.querySelectorAll('.bc-card, .bc-country, .bc-stat, .bc-section, .bc-section-grid, .bc-section-photo, .mw-parser-output > h2, .mw-parser-output > p'));
if (!items.length || !('IntersectionObserver' in window)) {
items.forEach(function (item) { item.classList.add('is-visible'); });
return;
}
items.forEach(function (item) { item.classList.add('bc-reveal'); });
var observer = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.classList.add('is-visible');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.12 });
items.forEach(function (item) { observer.observe(item); });
}
}


onReady(function () {
onReady(function () {
document.documentElement.classList.add('bc-ready');
document.documentElement.classList.add('bc-ready');
removeNoopServiceWorker();
prioritizeMedia();
initParticles();
initParticles();
makeSearchInteractive();
makeSearchInteractive();
animateStats();
animateStats();
enhanceCards();
addCardFilter();
addReadingTools();
revealContent();
});
});
})();
})();

Текущая версия от 04:10, 26 мая 2026

(function () {
	'use strict';

	function onReady(fn) {
		if (document.readyState === 'loading') {
			document.addEventListener('DOMContentLoaded', fn);
			return;
		}
		fn();
	}

	function loadScript(src, done) {
		if (document.querySelector('script[src="' + src + '"]')) {
			done();
			return;
		}
		var script = document.createElement('script');
		script.src = src;
		script.async = true;
		script.onload = done;
		document.head.appendChild(script);
	}

	function initParticles() {
	var hero = document.querySelector('.bc-hero');
	if (!hero) {
		return;
	}

	var oldHost = document.getElementById('bc-particles');
	if (oldHost) {
		oldHost.remove();
	}

	var host = document.createElement('div');
	host.id = 'bc-particles';
	hero.insertBefore(host, hero.firstChild);

	loadScript(
		'https://cdn.jsdelivr.net/npm/particles.js@2.0.0/particles.min.js',
		function () {
			if (!window.particlesJS) {
				return;
			}

			window.particlesJS('bc-particles', {
				particles: {
					number: {
						value: 110,
						density: {
							enable: true,
							value_area: 850
						}
					},

					color: {
						value: [
							'#1d5f9f',
							'#e40046',
							'#33c4ff',
							'#102033'
						]
					},

					shape: {
						type: 'circle'
					},

					opacity: {
						value: 0.38,
						random: true
					},

					size: {
						value: 4.4,
						random: true
					},

					line_linked: {
						enable: true,
						distance: 180,
						color: '#1d5f9f',
						opacity: 0.38,
						width: 1.5
					},

					move: {
						enable: true,
						speed: 1.0,
						direction: 'none',
						random: true,
						straight: false,
						out_mode: 'out',
						bounce: false
					}
				},

				interactivity: {
					detect_on: 'canvas',

					events: {
						onhover: {
							enable: true,
							mode: 'grab'
						},

						onclick: {
							enable: true,
							mode: 'push'
						},

						resize: true
					},

					modes: {
						grab: {
							distance: 190,
							line_linked: {
								opacity: 0.4
							}
						},

						push: {
							particles_nb: 4
						}
					}
				},

				retina_detect: true
			});
		}
	);
}

	function makeSearchInteractive() {
		var slot = document.querySelector('[data-bc-search]');
		if (!slot) {
			return;
		}

		var input = document.createElement('input');
		input.className = 'bc-search-input';
		input.type = 'search';
		input.autocomplete = 'off';
		input.placeholder = window.innerWidth < 560 ? 'страна, сервис или мера...' : slot.textContent.trim();
		input.setAttribute('aria-label', 'Поиск по BRICSCompass');
		input.placeholder = window.innerWidth < 560 ? '\u0441\u0442\u0440\u0430\u043d\u0430 \u0438\u043b\u0438 \u0441\u0435\u0440\u0432\u0438\u0441' : '\u043d\u0430\u0439\u0442\u0438 \u0441\u0442\u0440\u0430\u043d\u0443, \u0441\u0435\u0440\u0432\u0438\u0441 \u0438\u043b\u0438 \u043c\u0435\u0440\u0443';
		input.setAttribute('aria-label', '\u041f\u043e\u0438\u0441\u043a \u043f\u043e BRICSCompass');
		input.placeholder = window.innerWidth < 560 ? '\u0421\u0442\u0440\u0430\u043d\u0430 \u0438\u043b\u0438 \u0441\u0435\u0440\u0432\u0438\u0441' : '\u0421\u0442\u0440\u0430\u043d\u0430, \u0441\u0435\u0440\u0432\u0438\u0441 \u0438\u043b\u0438 \u043c\u0435\u0440\u0430';
		slot.replaceChildren(input);
		slot.classList.add('is-live');

		var cards = Array.prototype.slice.call(document.querySelectorAll('.bc-card, .bc-country'));
		var tags = Array.prototype.slice.call(document.querySelectorAll('[data-bc-query]'));
		var arrow = document.querySelector('.bc-arrow');

		function filter(value) {
			var query = value.trim().toLowerCase();
			cards.forEach(function (card) {
				var text = card.textContent.toLowerCase();
				var match = !query || text.indexOf(query) !== -1;
				card.classList.toggle('is-match', !!query && match);
				card.classList.toggle('is-dim', !!query && !match);
			});
			tags.forEach(function (tag) {
				tag.classList.toggle('is-active', query && tag.getAttribute('data-bc-query').toLowerCase() === query);
			});
		}

		input.addEventListener('input', function () {
			filter(input.value);
		});

		tags.forEach(function (tag) {
			tag.addEventListener('click', function () {
				input.value = tag.getAttribute('data-bc-query') || '';
				filter(input.value);
				input.focus();
			});
		});

		function getCardLink(card) {
			var link = card.closest('a');
			if (link && link.href) {
				return link.href;
			}
			return '';
		}

		function submitSearch() {
			var query = input.value.trim().toLowerCase();
			var target = null;
			if (!query) {
				input.focus();
				return;
			}
			cards.some(function (card) {
				var titleNode = card.querySelector('strong');
				var title = (titleNode || card).textContent.trim().toLowerCase();
				var text = card.textContent.trim().toLowerCase();
				if (title === query) {
					target = card;
					return true;
				}
				if (!target && text.indexOf(query) !== -1) {
					target = card;
				}
				return false;
			});
			var href = target && getCardLink(target);
			if (href) {
				window.location.href = href;
				return;
			}
			window.location.href = (mw.config.get('wgScriptPath') || '') + '/index.php?search=' + encodeURIComponent(input.value.trim());
		}

		input.addEventListener('keydown', function (event) {
			if (event.key === 'Enter') {
				event.preventDefault();
				submitSearch();
			}
		});

		if (arrow) {
			arrow.setAttribute('role', 'button');
			arrow.setAttribute('tabindex', '0');
			arrow.setAttribute('aria-label', 'Найти раздел');
			arrow.addEventListener('click', submitSearch);
			arrow.addEventListener('keydown', function (event) {
				if (event.key === 'Enter' || event.key === ' ') {
					event.preventDefault();
					submitSearch();
				}
			});
		}
	}

	function animateStats() {
		var stats = Array.prototype.slice.call(document.querySelectorAll('.bc-stat-value'));
		if (!stats.length || !('IntersectionObserver' in window)) {
			return;
		}

		var observer = new IntersectionObserver(function (entries) {
			entries.forEach(function (entry) {
				if (!entry.isIntersecting) {
					return;
				}
				var node = entry.target;
				var target = parseInt(node.textContent.replace(/\D/g, ''), 10);
				if (!target || node.getAttribute('data-animated')) {
					return;
				}
				node.setAttribute('data-animated', 'true');
				var start = performance.now();
				var duration = 900;
				function tick(now) {
					var progress = Math.min((now - start) / duration, 1);
					var eased = 1 - Math.pow(1 - progress, 3);
					node.lastChild.nodeValue = String(Math.round(target * eased));
					if (progress < 1) {
						requestAnimationFrame(tick);
					}
				}
				node.textContent = '0';
				requestAnimationFrame(tick);
				observer.unobserve(node);
			});
		}, { threshold: 0.35 });

		stats.forEach(function (stat) {
			observer.observe(stat);
		});
	}

	function prioritizeMedia() {
		Array.prototype.forEach.call(document.querySelectorAll('.bc-photo img, .bc-section-photo img'), function (img) {
			img.loading = 'eager';
			img.decoding = 'async';
			img.setAttribute('fetchpriority', 'high');
		});
	}

	function removeNoopServiceWorker() {
		if (!('serviceWorker' in navigator) || !navigator.serviceWorker.getRegistrations) {
			return;
		}
		navigator.serviceWorker.getRegistrations().then(function (registrations) {
			registrations.forEach(function (registration) {
				var url = registration.active && registration.active.scriptURL;
				if (url && url.indexOf('skins.citizen.serviceWorker') !== -1) {
					registration.unregister();
				}
			});
		}).catch(function () {});
	}

	function enhanceCards() {
		var cards = Array.prototype.slice.call(document.querySelectorAll('.bc-card, .bc-country, .bc-stat'));
		cards.forEach(function (card) {
			card.setAttribute('tabindex', '0');
			card.addEventListener('mousemove', function (event) {
				var rect = card.getBoundingClientRect();
				card.style.setProperty('--mx', Math.round(event.clientX - rect.left) + 'px');
				card.style.setProperty('--my', Math.round(event.clientY - rect.top) + 'px');
			});
			card.addEventListener('keydown', function (event) {
				var link = card.closest('a');
				if (link && (event.key === 'Enter' || event.key === ' ')) {
					event.preventDefault();
					link.click();
				}
			});
		});
	}

	function addCardFilter() {
		var grid = document.querySelector('.bc-card-grid');
		if (!grid || document.querySelector('.bc-hero') || document.querySelector('.bc-local-filter')) {
			return;
		}
		var cards = Array.prototype.slice.call(grid.querySelectorAll('.bc-card'));
		if (cards.length < 3) {
			return;
		}
		var panel = document.createElement('div');
		panel.className = 'bc-local-filter';
		panel.innerHTML =
			'<div class="bc-local-filter-title">\u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0444\u0438\u043b\u044c\u0442\u0440</div>' +
			'<input type="search" class="bc-local-filter-input" placeholder="\u0424\u0438\u043b\u044c\u0442\u0440 \u043f\u043e \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435" aria-label="\u0424\u0438\u043b\u044c\u0442\u0440 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a">' +
			'<div class="bc-local-filter-count"></div>';
		grid.parentNode.insertBefore(panel, grid);
		var input = panel.querySelector('.bc-local-filter-input');
		var count = panel.querySelector('.bc-local-filter-count');
		function update() {
			var query = input.value.trim().toLowerCase();
			var visible = 0;
			cards.forEach(function (card) {
				var match = !query || card.textContent.toLowerCase().indexOf(query) !== -1;
				card.classList.toggle('is-hidden', !match);
				if (match) {
					visible += 1;
				}
			});
			count.textContent = visible + ' / ' + cards.length;
		}
		input.addEventListener('input', update);
		update();
	}

	function addReadingTools() {
		if (document.querySelector('.bc-read-progress')) {
			return;
		}
		var progress = document.createElement('div');
		progress.className = 'bc-read-progress';
		document.body.appendChild(progress);
		var top = document.createElement('button');
		top.className = 'bc-top-button';
		top.type = 'button';
		top.setAttribute('aria-label', '\u041d\u0430\u0432\u0435\u0440\u0445');
		top.textContent = '\u2191';
		document.body.appendChild(top);
		top.addEventListener('click', function () {
			window.scrollTo({ top: 0, behavior: 'smooth' });
		});
		function update() {
			var doc = document.documentElement;
			var max = Math.max(1, doc.scrollHeight - window.innerHeight);
			var ratio = Math.min(1, Math.max(0, window.scrollY / max));
			progress.style.transform = 'scaleX(' + ratio + ')';
			top.classList.toggle('is-visible', window.scrollY > 520);
		}
		window.addEventListener('scroll', update, { passive: true });
		window.addEventListener('resize', update);
		update();
	}

	function revealContent() {
		var items = Array.prototype.slice.call(document.querySelectorAll('.bc-card, .bc-country, .bc-stat, .bc-section, .bc-section-grid, .bc-section-photo, .mw-parser-output > h2, .mw-parser-output > p'));
		if (!items.length || !('IntersectionObserver' in window)) {
			items.forEach(function (item) { item.classList.add('is-visible'); });
			return;
		}
		items.forEach(function (item) { item.classList.add('bc-reveal'); });
		var observer = new IntersectionObserver(function (entries) {
			entries.forEach(function (entry) {
				if (entry.isIntersecting) {
					entry.target.classList.add('is-visible');
					observer.unobserve(entry.target);
				}
			});
		}, { threshold: 0.12 });
		items.forEach(function (item) { observer.observe(item); });
	}

	onReady(function () {
		document.documentElement.classList.add('bc-ready');
		removeNoopServiceWorker();
		prioritizeMedia();
		initParticles();
		makeSearchInteractive();
		animateStats();
		enhanceCards();
		addCardFilter();
		addReadingTools();
		revealContent();
	});
})();