MediaWiki:Gadget-ZoomableMap.js

/* * ZoomableMap v1.6 * Copyright (c) 2022 Jesús Martínez (User:Ciencia_Al_Poder) * * Permite hacer scroll y zoom en una imagen grande dentro de una página * * This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *   the Free Software Foundation; either version 2 of the License, or *   (at your option) any later version (function( $ ) { 'use strict';

var _sensibility = 2, _init = function { $( '.zoomablemap' ).each( function {		$( this ).data( 'zoomablemap', new ZoomableMap( this ) );	} ); }, _initZoomableMap = function { var $controls, $zoomin, $zoomout, $contents, $toRemove = null; $contents = this.$el.children; // imagemap wraps everything in a div.noresize, which causes problems when dragging. Remove the extra element if ( $contents.length === 1 && $contents.is( '.noresize' ) ) { $toRemove = $contents; $contents = $contents.children; }	this.$mapCont = $( ' ' ).append( $(' ').append( $contents ) ); if ( $toRemove ) { $toRemove.remove; }	$controls = $( ' ' ); $zoomin = $( ' ' ).text( '+' ).on( 'click', _eventDelegate( this, _zoomIn ) ); $zoomout = $( ' ' ).text( '-' ).on( 'click', _eventDelegate( this, _zoomOut ) ); $controls.append( $zoomin, $zoomout ); this.$el.append( this.$mapCont, $controls ); this.$mapCont.on( {		pointerdown: _eventDelegate( this, _pointerDown ),		wheel: _eventDelegate( this, _wheel )	} ); _updateScale.call( this, -1 ); this.$el.addClass( 'loadcomplete' ); }, _eventDelegate = function( thisArg, fn ) { return function( e ) { return fn.call( thisArg, e ); }; }, _zoomIn = function { _updateScale.call( this, 0.1 ); return false; }, _zoomOut = function { _updateScale.call( this, -0.1 ); return false; }, _wheel = function( e ) { _updateScale.call( this, e.originalEvent.deltaY > 0 ? -0.1 : 0.1 ); return false; }, _updateScale = function( delta ) { var rect, newScale, minScale, innerDimensions; newScale = this.scale + delta; if ( newScale < 0.1 ) { newScale = 0.1; }	innerDimensions = _getInnerDimensions.call( this ); // If the element is hidden (collapsible sections) this will be an empty array rect = this.$mapCont[0].getClientRects; if ( !rect || rect.length === 0 ) { return; }	// Lower limit is to fit the contents on the current width minScale = Math.min( rect[0].width / innerDimensions.width, rect[0].height / innerDimensions.height ); if ( newScale < minScale ) { newScale = minScale; }	if ( newScale > 1 ) { newScale = 1; }	this.scale = newScale; this.$mapCont.find( '> .mapinner' ).eq( 0 ).css( {		transform: 'scale(' + String( newScale ) + ')',		height: String( innerDimensions.height * newScale ) + 'px'	} ); }, _pointerDown = function( e ) { var pos = _getEventCoordinates( e, this.$mapCont[0] ); e = e.originalEvent; // Retarget all pointer events (until pointerup) this.$mapCont[0].setPointerCapture( e.pointerId ); if ( e.isPrimary ) { this.dragStartPoint = pos; this.pointerCache = []; this.initialPointerDistance = 0; this.$mapCont.on( {			'pointermove': _eventDelegate( this, _dragMove ),			'pointerup': _eventDelegate( this, _dragEnd )		} ); this.$mapCont.addClass( 'pointerdown' ); }	// Allow max 2 pointers if ( this.pointerCache !== null && this.pointerCache.length < 2 ) { this.pointerCache.push( { id: e.pointerId, x: pos.x, y: pos.y } ); // On 2nd pointer, set initial zoom position if ( !e.isPrimary ) { this.initialPointerDistance = _getPointDistance.call( this ); this.initialPinchZoomScale = this.scale; }	}	return false; }, _dragMove = function( e ) { var newPos, newProps, newDistance; newPos = _getEventCoordinates( e, this.$mapCont[0] ); e = e.originalEvent; // This should never happen if ( this.pointerCache === null ) { return; }	// Update point cache for ( var i = 0; i < this.pointerCache.length; i++ ) { if ( this.pointerCache[ i ].id == e.pointerId ) { this.pointerCache[ i ].x = newPos.x;			this.pointerCache[ i ].y = newPos.y;			break; }	}	if ( e.isPrimary ) { newProps = { scrollLeft: this.dragStartPoint.scrollLeft - newPos.x + this.dragStartPoint.x,			scrollTop: this.dragStartPoint.scrollTop - newPos.y + this.dragStartPoint.y		}; this.$mapCont.prop( newProps ); }	if ( this.pointerCache.length == 2 && this.initialPointerDistance > 0 ) { newDistance = _getPointDistance.call( this ); _updateScale.call( this, this.initialPinchZoomScale * newDistance / this.initialPointerDistance - this.scale ); }	if ( !this.dragging ) { this.$mapCont.addClass( 'dragging' ); this.dragging = true; } }, _dragEnd = function( e ) { var newPos, propagate = false; e = e.originalEvent; if ( !this.pointerCache ) { return; }	// Remove pointer from cache for ( var i = 0; i < this.pointerCache.length; i++ ) { if ( this.pointerCache[ i ].id == e.pointerId ) { this.pointerCache.splice( i, 1 ); break; }	}	if ( !e.isPrimary ) { return; }	newPos = _getEventCoordinates( e, this.$mapCont[0] ); // If it hasn't scrolled, propagate the event if (		Math.abs( newPos.x - this.dragStartPoint.x ) <= _sensibility &&		Math.abs( newPos.y - this.dragStartPoint.y ) <= _sensibility	) { propagate = true; }	this.$mapCont.off( 'pointermove pointerup' ).removeClass( 'pointerdown' ); this.dragStartPoint = null; this.dragging = false; this.$mapCont.removeClass( 'dragging' ); this.pointerCache = null; if ( propagate ) { this.$el.trigger( $.Event( 'mapclick', { clientX: e.clientX, clientY: e.clientY } ) ); } }, _getPointDistance = function { var xx, yy; xx = this.pointerCache[ 0 ].x - this.pointerCache[ 1 ].x;	yy = this.pointerCache[ 0 ].y - this.pointerCache[ 1 ].y;	return Math.sqrt( xx * xx + yy * yy ); }, /* Given an event and element, returns pointer coordinates and scroll position of element */ _getEventCoordinates = function( event, el ) { return { x: event.clientX, y: event.clientY, scrollLeft: el.scrollLeft, scrollTop: el.scrollTop }; }, _getInnerDimensions = function { var innerEl; if ( !this.innerDimensions ) { innerEl = this.$mapCont.find( '> .mapinner' )[0]; if ( innerEl.scrollWidth === 0 || innerEl.scrollHeight === 0 ) { return null; }		this.innerDimensions = { width: innerEl.scrollWidth, height: innerEl.scrollHeight }; }	return this.innerDimensions; };

// Class ZoomableMap function ZoomableMap( el ) { this.$el = $( el ); this.$mapCont = null; this.dragStartPoint = null; this.dragging = false; this.scale = 1; this.pointerCache = null; this.innerDimensions = null; this.initialPointerDistance = 0; this.initialPinchZoomScale = 0; _initZoomableMap.call( this ); }

$(_init);

})( jQuery );