Участник:IKhitron/testpopup.js

(function () {
	let winwidth;														// ширина текущего окна браузера
	let winheight;														// высота текущего окна браузера
	const imagewidth = 1320;											// ширина файла изображения
	const imageheight = 1900;											// высота файла изображения
	const minimagewidth = 900;											// минимальная ширина показанного изображения
	const minimageheight = imageheight * minimagewidth / imagewidth;	// минимальная высота показанного изображения
	const minlistwidth = 200;											// минимальная ширина подокна списка ссылок
	let isvertical;														// флаг, показывающий, находится ли список рядом с изображением, или под ним
	let multiplier = 1;													// мультипликатор увеличения изображения
	let originalmap;													// бэкап копия тега imagemap

	/*
		Пересоздание структуры в зависимости от новых данных
		leftwidth	текущая ширина подокна изображения
		rightwidth	текущая ширина подокна списка ссылок
		upheight	текущая высота подокна изображения
		downheight	текущая высота подокна списка ссылок
	*/
	function resize(leftwidth = $('#mapwrapper').width(), rightwidth = $('#listwrapper').width(),
			upheight = $('#mapwrapper').height(), downheight = $('#listwrapper').height()) {
		const mapwidth = Math.floor((Math.max(minimagewidth, leftwidth) - 10) * multiplier);				// текущая ширина видимого изображения
		const mapheight = Math.floor(mapwidth * imageheight / imagewidth);									// текущая высота видимого изображения
		const colored = $('.liHighlighting a');																// текущая выделенная ссылка в списке ссылок
		$('#mycontent #mapwrapper').width(leftwidth);														// установить ширину элемента, содержащего mapplace
		$('#mycontent #mapwrapper').height(upheight);														// установить высоту элемента, содержащего mapplace
		$('#mycontent #mapplace').width(leftwidth);															// установить ширину элемента, содержащего изображение
		$('#mycontent #mapplace').height(upheight);															// установить высоту элемента, содержащего изображение
		$('#mycontent #map').width(mapwidth);																// установить ширину изображения
		$('#mycontent #map').height(mapheight);																// установить высоту изображения
		$('#mycontent #map div').width(mapwidth);															// установить ширину элемента, содержащего подсветку изображения
		$('#mycontent #map div').height(mapheight);															// установить высоту элемента, содержащего подсветку изображения
		$($('#mycontent #map div div')[0]).width(mapwidth);													// установить ширину элемента, частично содержащего подсветку изображения
		$($('#mycontent #map div div')[0]).height(mapheight);												// установить высоту элемента, частично содержащего подсветку изображения
		$($('#mycontent #map div div')[0]).children().width(mapwidth);										// установить ширину элементов внутри элемента, частично содержащего подсветку изображения
		$($('#mycontent #map div div')[0]).children().height(mapheight);									// установить высоту элементов внутри элемента, частично содержащего подсветку изображения
		$($('#mycontent #map div div')[0]).children().attr({width: mapwidth, height: mapheight});			// установить атрибуты размеров элементов внутри элемента, частично содержащего подсветку изображения
		$('#mycontent #listwrapper').width(rightwidth);														// установить ширину элемента, содержащего listplace
		$('#mycontent #listwrapper').height(downheight);													// установить высоту элемента, содержащего listplace
		$('#mycontent #listplace').width(rightwidth - 20);													// установить ширину элемента, содержащего список ссылок
		$('#mycontent #listplace').height(downheight - 20);													// установить высоту элемента, содержащего список ссылок
		$('#mycontent #list').width(rightwidth - 20);														// установить ширину списка ссылок
		$('#mycontent #list').height(downheight - 20);														// установить высоту списка ссылок
		$('#mycontent #list div').width(rightwidth - 20);													// установить ширину элемента, обхватывающего список ссылок
		$('#mycontent #list div').height(downheight - 20);													// установить высоту элемента, обхватывающего список ссылок
		recalculatemap(colored);																			// вычислить новые координаты подсветок
		if (Math.abs($('#mycontent').width() - $('#mapwrapper').width()) < 20) {							// в случае, если надо вертикально
			isvertical = true;																				// установить, что будет вертикально
		    $(".ui-resizable-s").on({																		// настроить мышь и тачскрин
		        mouseenter: function () {																	// при наведении мыши
		            $(this).css('background-image', 'linear-gradient(green , yellow)');						// показать жёлто-зелёную горизонтальную разделительную черту
		        },  
		        mouseleave: function () {																	// при убирании мыши
		            $(this).css('background-image', 'initial');												// убрать горизонтальную разделительную черту
		        },
		        touchstart: function () {																	// при нажатии на тачскрин
		        	$(this).trigger('mouseenter');															// запустить событие наведения мыши
		        },
		        touchmove: OO.ui.debounce(function (event) {												// при движении пальца, нажатого на тачскрин
		        	let y = Math.min(Math.max(Math.floor(event.touches[0].clientY), 25), winheight - 25);	// вычислить разницу вертикального сдвига
		        	resize(undefined, undefined, y, winheight - y);											// переформатировать структуру соответственно
		        }, 100),																					// сколько ждать
		        touchend: function () {																		// при отпускании тачскрина
		        	$(this).trigger('mouseleave');															// запустить событие убирания мыши
		        }
		    });
		    $(".ui-resizable-e").on('mouseenter mouseleave', function () {									// для вертикальной разделительной черты
		            $(this).css('background-image', 'initial');												// спрятать, как ненужную												
		        });
			$('.ui-resizable-s').css('cursor', 's-resize');													// установить курсор для горизонтальной разделительной черты
			$('.ui-resizable-e').css('cursor', 'default');													// восстановить курсор для вертикальной разделительной черты
			$('#mapwrapper').resizable('option', {															// для элемента, содержащего изображение, установить
				maxHeight: winheight - 25,																	// максимальную высоту
				minHeight: 25,																				// минимальную высоту
				maxWidth: leftwidth,																		// максимальную ширину
				minWidth: leftwidth,																		// минимальную ширину
				handles: 's',																				// определить горизонтальную разделительную черту
				resize: function(e, ui) {																	// функция смены размера для мыши
					resize(undefined, undefined, undefined, winheight - $('#mapwrapper').height());			// переформатировать структуру соответственно
				}
			});
		} else {																							// в случае, если надо горизонтально
			isvertical = false;																				// установить, что будет горизонтально
		    $(".ui-resizable-e").on({																		// настроить мышь и тачскрин
		        mouseenter: function () {																	// при наведении мыши
		            $(this).css('background-image', 'linear-gradient(to right, green , yellow)');			// показать жёлто-зелёную вертикальную разделительную черту
		        },  
		        mouseleave: function () {																	// при убирании мыши
		            $(this).css('background-image', 'initial');												// убрать вертикальную разделительную черту
		        },
		        touchstart: function () {																	// при нажатии на тачскрин
		        	$(this).trigger('mouseenter');															// запустить событие наведения мыши
		        },
		        touchmove: OO.ui.debounce(function (event) {												// при движении пальца, нажатого на тачскрин
		        	let x = Math.min(Math.max(Math.floor(event.touches[0].clientX), 25), winwidth - 25);	// вычислить разницу горизонтального сдвига
		        	resize(x, winwidth - x);																// переформатировать структуру соответственно
		        }, 100),																					// сколько ждать
		        touchend: function () {																		// при отпускании тачскрина
		        	$(this).trigger('mouseleave');															// запустить событие убирания мыши
		        }
		    });
		    $(".ui-resizable-s").on('mouseenter mouseleave', function () {									// для горизонтальной разделительной черты
		            $(this).css('background-image', 'initial');												// спрятать, как ненужную
		        });
			$('.ui-resizable-s').css('cursor', 'default');													// восстановить курсор для горизонтальной разделительной черты
			$('.ui-resizable-e').css('cursor', 'e-resize');													// установить курсор для вертикальной разделительной черты
			$('#mapwrapper').resizable('option', {															// для элемента, содержащего изображение, установить
				maxHeight: upheight,																		// максимальную высоту
				minHeight: upheight,																		// минимальную высоту
				maxWidth: winwidth - 25,																	// максимальную ширину
				minWidth: 25,																				// минимальную ширину
				handles: 'e',																				// определить вертикальную разделительную черту
				resize: function(e, ui) {																	// функция смены размера для мыши
					resize(undefined, winwidth - $('#mapwrapper').width());									// переформатировать структуру соответственно
				}
			});
		}
	}

	/*
		Одна итерация работы при возможной смене размеров окна браузера
	*/
	function test() {
		let svgwidth;																	// ширина изображения
		let newwinwidth = Math.floor($('.mw-parser-output').width());					// новая ширина текущего окна браузера
		let newwinheight = $(window).height() - 30;										// новая высота текущего окна браузера
		if (newwinwidth == winwidth && newwinheight == winheight)						// если нет изменений
			return;																		// ничего не делать
		winwidth = newwinwidth;															// установить ширину текущего окна браузера
		winheight = newwinheight;														// установить высоту текущего окна браузера
		if (winwidth <= minimagewidth + minlistwidth) {									// 1. если изображение и список ссылок не помещаются в ширину
			svgwidth = Math.max(minimagewidth, winwidth);								// вычислить ширину изображения
			const svgheight = Math.floor(svgwidth * imageheight / imagewidth);			// вычислить высоту изображения
			const upheight = Math.min(svgheight, Math.floor(0.8 * winheight));			// вычислить высоту подокна изображения
			resize(winwidth, winwidth, upheight, winheight - upheight);					// переформатировать структуру соответственно
		} else if (winheight <= minimageheight)											// 2. если нет, и изображение не помещается в высоту
			resize(minimagewidth, winwidth - minimagewidth, winheight, winheight);		// переформатировать структуру соответственно
		else if ((winwidth - minlistwidth) / winheight > imagewidth / imageheight) {	// 3. если нет, и ширина места для изображения больше, чем потребовалось, если изменить размер 
																						//		изображения под высоту окна браузера
			svgwidth = Math.floor(winheight * imagewidth / imageheight);				// вычислить ширину изображения
			resize(svgwidth, winwidth - svgwidth, winheight, winheight);				// переформатировать структуру соответственно
		} else {																		// 4. если нет
			svgwidth = winwidth - minlistwidth;											// вычислить ширину изображения
			resize(svgwidth, minlistwidth, winheight, svgwidth);						// переформатировать структуру соответственно
		}
	}

	/*
		Вычислить новые координаты подсветок
		colored		текущая выделенная ссылка
	*/
	function recalculatemap(colored) {
		let curmap = $('.imgtogglefull map');
		let f = function (item, index, arr) {
			arr[index] = Math.round(item * $('#map div div').width() / (minimagewidth - 10));
		};
		for (let i = 0; i < curmap.find('area').length; i++) {
			let tmparr = originalmap.find('area')[i].coords.split(',');
			tmparr.forEach(f);
			$(curmap.find('area')[i]).attr('coords', tmparr.join(','));
		}
		$('body').trigger('refresh-imagehighlight-links1');
		colored.trigger('mouseenter');
	}
	
	function build() {
		new mw.Api().get({
			action: 'parse',
			contentmodel: 'wikitext',
			format: 'json',
			formatversion: 2,
			prop: 'text',
			text: '{{#invoke:MetroMap/песочница|tempimagemap|Московского|Empty svg for Moscow metro map.svg|pagename=a}}'
		}).done(function(data) {
			let f1 = $('<div>', {
				id: 'mapwrapper'
			});
			let f2 = $('<div>', {
				id: 'mapplace'
			}).css('overflow', 'auto');
			let f3 = $(data.parse.text).attr({
				id: 'map'
			});
			let l1 = $('<div>', {
				id: 'listwrapper'
			});
			let l2 = $('<div>', {
				id: 'listplace'
			}).css({
				overflowY: 'auto',
				margin: '10px'
			});
			let l3 = $('<div>', {
				id: 'list'
			});
			
			f3.find('img').attr({
				srcset: '',
				src: '//upload.wikimedia.org/wikipedia/ru/f/f8/Moscow_metro_map_sb_local_draft.svg'
			});
			$('#mycontent').css({
				display: 'flex',
				flexWrap: 'wrap'
			}).addClass('popup-table popup-version-opened-Московского ts-ИСММ ts-ОКИ')
				.append(f1.append(f2.append(f3)))
				.append(l1.append(l2.append(l3)));
			$('#mapwrapper').resizable({});
			$('#mapwrapper').on('wheel mousewheel DOMMouseScroll', function (event) {
				let parentOffset = $("#mapplace").offset();
				let x = event.pageX ? event.pageX - parentOffset.left : 0;
				let y = event.pageY ? event.pageY - parentOffset.top : 0;
			    let newmultiplier = event.deltaY > 0 || event.originalEvent.wheelDelta > 0 || event.originalEvent.detail < 0
			    	? Math.min(multiplier * 1.2, 8) : Math.max(multiplier * 0.8, 1);
			    let curmultiplier = newmultiplier / multiplier;
				if ((x > 30 && x < $('#mapplace').width() - 30 && y > 30 && y < $('#mapplace').height() - 30) || ! event.pageX) {
					multiplier = newmultiplier;
					resize();
					$('#mapplace')[0].scrollTo(Math.floor($("#mapplace").offset().left - $("#map").offset().left * curmultiplier + (event.pageX
						|| parentOffset.left) * (curmultiplier - 1)), Math.floor($("#mapplace").offset().top - $("#map").offset().top
						* curmultiplier + (event.pageY || parentOffset.top) * (curmultiplier - 1)), {behavior: 'instant'});
					event.preventDefault();
				}
			});
			originalmap = $('.imgtogglefull map').clone();
			$('#mapwrapper').on('mouseup', function (event) {
				if (event.which == 2)
					multiplier = 1;
					resize();
					event.preventDefault();
			});
			$('#mapwrapper').on('mousemove', function (e) {
				let parentOffset = $(this).parent().offset();
				let x = e.pageX - parentOffset.left;
				let y = e.pageY - parentOffset.top;
				if (x < 30)
					$('#mapplace')[0].scrollBy(-10, 0, {behavior: "smooth"});
				else if (x > $('#mapwrapper').width() - 30)
					$('#mapplace')[0].scrollBy(10, 0, {behavior: "smooth"});
				else if (y < 30)
					$('#mapplace')[0].scrollBy(0, -10, {behavior: "smooth"});
				else if (y > $('#mapwrapper').height() - 30)
					$('#mapplace')[0].scrollBy(0, 10, {behavior: "smooth"});
				event.preventDefault();
			});
			$('body').trigger('refresh-imagehighlight-links1');
			let scaling, dist, newdist;
			$('#mapwrapper').on({
				touchstart: function (event) {
					if (event.touches.length === 2) {
						scaling = true;
						dist = undefined;
						if (event.cancelable) 
							event.preventDefault();
					}
				},
				touchmove: function (event) {
					if (scaling) {
						newdist = Math.hypot(event.touches[0].pageX - event.touches[1].pageX, event.touches[0].pageY - event.touches[1].pageY);
		    			if (dist) {
							let parentOffset = $("#mapplace").offset();
							let x = Math.floor((event.touches[0].pageX + event.touches[1].pageX) / 2) - parentOffset.left;
							let y = Math.floor((event.touches[0].pageY + event.touches[1].pageY) / 2) - parentOffset.top;
			    			let newmultiplier = newdist > dist ? Math.min(multiplier * 1.2, 8) : Math.max(multiplier * 0.8, 1);
						    let curmultiplier = newmultiplier / multiplier;
							multiplier = newmultiplier;
							resize();
							$('#mapplace')[0].scrollTo(Math.floor($("#mapplace").offset().left - $("#map").offset().left * curmultiplier + x
								* (curmultiplier - 1)), Math.floor($("#mapplace").offset().top - $("#map").offset().top * curmultiplier + y
								* (curmultiplier - 1)), {behavior: 'instant'});
		    			}
						dist = newdist;
						if (event.cancelable) 
							event.preventDefault();
					}
				},
				touchend: function (event) {
					if (scaling) {
						scaling = false;
						if (event.cancelable) 
							event.preventDefault();
					}
				}
			});
			$('#list').html($('.mw-collapsible'));
			test();
			keys();
		});
	}
	
	function keys() {
		let cur, next;
		$(document).on('keydown', function (event) {
			switch (event.which) {
				case 65: // a
					if (! isvertical) {
						cur = $('#mycontent #mapwrapper').width();
						next = Math.max(25, cur - 50);
						resize(next, winwidth - next);
						event.preventDefault();
					}
					break;
				case 68: // d
					if (! isvertical) {
						cur = $('#mycontent #mapwrapper').width();
						next = Math.min(winwidth - 25, cur + 50);
						resize(next, winwidth - next);
						event.preventDefault();
					}
					break;
				/*case 69: // e tmp
					mw.notify(`${$('#mapplace').scrollLeft()},${$('#mapplace')[0].offsetWidth},${$('#mapplace')[0].clientWidth},${$('#mapplace').width()},`);
					break;*/
				case 87: // s
					if (isvertical) {
						cur = $('#mycontent #mapwrapper').height();
						next = Math.max(25, cur - 50);
						resize(undefined, undefined, next, winheight - next);
						event.preventDefault();
					}
					break;
				case 83: // w
					if (isvertical) {
						cur = $('#mycontent #mapwrapper').height();
						next = Math.min(winheight - 25, cur + 50);
						resize(undefined, undefined, next, winheight - next);
						event.preventDefault();
					}
					break;
				case 61:
				case 187: // +
					multiplier = Math.min(multiplier * 1.2, 8);
					resize();
					event.preventDefault();
					break;
				case 48: // 0
					multiplier = 1;
					resize();
					event.preventDefault();
					break;
				case 173:
				case 189: // -
					multiplier = Math.max(multiplier * 0.8, 1);
					resize();
					event.preventDefault();
					break;
				case 37: // <-
					if (event.shiftKey)
						$('#mapplace')[0].scrollBy(-10, 0, {behavior: "smooth"});
					break;
				case 38: // |^
					if (event.shiftKey)
						$('#mapplace')[0].scrollBy(0, -10, {behavior: "smooth"});
					break;
				case 39: // ->
					if (event.shiftKey)
						$('#mapplace')[0].scrollBy(10, 0, {behavior: "smooth"});
					break;
				case 40: { // |v
					if (event.shiftKey)
					$('#mapplace')[0].scrollBy(0, 10, {behavior: "smooth"});
					break;
				}
				default:
			}
		});
}
	
	mw.loader.using( ['oojs-ui-core', 'jquery.ui', 'mediawiki.api'], function() {
		if (mw.config.get('wgPageName') === 'Участник:IKhitron/testpopup' && $('.mw-parser-output').length) {
			$('.vector-sticky-header-container').css({ display: 'none' });
			mw.util.addCSS('.ui-icon {visibility:hidden;}');
			mw.util.addCSS('#mapwrapper {touch-action: none}');
			$(build);
			$(window).on('resize orientationchange', OO.ui.debounce(test, 1000));
		}
	});
})();
Prefix: a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9

Portal di Ensiklopedia Dunia

Kembali kehalaman sebelumnya