MediaWiki:InteractiveMaps.js

da Pokémon Central Wiki, l'enciclopedia Pokémon in italiano.
Vai alla navigazione Vai alla ricerca

Nota: dopo aver salvato, potrebbe essere necessario pulire la cache del proprio browser per vedere i cambiamenti.

  • Firefox / Safari: tenere premuto il tasto delle maiuscole Shift e fare clic su Ricarica, oppure premere Ctrl-F5 o Ctrl-R (⌘-R su Mac)
  • Google Chrome: premere Ctrl-Shift-R (⌘-Shift-R su un Mac)
  • Internet Explorer: tenere premuto il tasto Ctrl e fare clic su Aggiorna, oppure premere Ctrl-F5
  • Opera: Vai nel Menu → Impostazioni (Opera → Preferenze su un Mac) e poi in Privacy & sicurezza → Pulisci dati del browser → Immagini e file nella cache.
'use strict';
/*
 * ZoomableMap v1.5
 * Copyright (c) 2022 Jesús Martínez (User:Ciencia_Al_Poder)
 *
 * Allow zooming and scrolling of an image within a page.
 * 
 * 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( $ ) {
var _sensibility = 2,
    _init = function() {
    $( '.zoomablemap' ).each( function() {
        $( this ).data( 'zoomablemap', new ZoomableMap( this ) );
    } );
},
_initZoomableMap = function() {
    var $controls, $zoomin, $zoomout;
    this.$mapCont = $( '<div class="mapcontent"></div>' ).append( $('<div class="mapinner"></div>').append( this.$el.contents() ) );
    $controls = $( '<div class="mapcontrols"></div>' );
    $zoomin = $( '<div class="control zoomin" tabindex="0" role="button"></div>' ).text( '+' ).on( 'click', _eventDelegate( this, _zoomIn ) );
    $zoomout = $( '<div class="control zoomout" tabindex="0" role="button"></div>' ).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);

})( $ );

/*******************************************************************************
 * LPAInteractiveMaps
 * Copyright (c) 2022 Pokémon Central
 *
 * Interactive maps for Leggende Pokémon: Arceus on Pokémon Central Wiki
 *
 * This program is heavily based on MapaLPAInteractivo v1.5
 * (https://www.wikidex.net/wiki/MediaWiki:Gadget-MapaLPAInteractivo.js) by
 * Jesús Martínez (User:Ciencia_Al_Poder). All due credits to the original
 * author.
 *
 * 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( $ ) {
// WARNING: Doesn't support multiple maps in a page
var _buckets = {},
_mapData = {},
_svg = null,
_downCoords = null,
_sensibility = 5,
_pointRadius = 15,
_originalWidth = 2048,
_originalHeight = 2048,
_selectedPoint = null,
_useMapUpFallback = true,
_expandedPointTypes = {
    s: 'm_estrella',
    a: 'm_arbol',
    c: 'm_caja',
    r: 'm_roca',
    w: 'm_distorsion'
},
_init = function() {
    $( '#interactive-map-controller' ).each( _initializeTable );
},
_initializeTable = function() {
    var $table = $( this ), origTablePosition, localPoints;
    // This thing of detaching and reattaching the table is used to remove the
    // table if anything goes wrong during the load
    origTablePosition = _detachElement( $table );
    try {
        $table.find( '> tbody > tr[data-principal]' ).each( function() {
            var $tr = $( this ), $td = $tr.find( '> td:eq(2)' );
            if ( $tr.data( 'principal' ) === 1 ) {
                $( '<label class="det"><input type="checkbox"/><span>Dettagli</span></label> ' ).prependTo( $td );
            }
            $( '<label class="map"><input type="checkbox"/><span>Mappa</span></label> ' ).prependTo( $td );
        } ).filter( '[data-principal="0"]' ).each( function() {
            // Hide secondary
            var $tr = $( this ).hide(), $prev = $tr.prev();
            localPoints = _parsePointData( $tr.data( 'pointdata' ) );
            // Generate ids for coordinates
            $tr.data( 'ids', $.map( localPoints, function( el ) {
                return el.id;
            } ).join( ',' ) );
        } );
        $table.on( 'change', _onChange );
        _generateMap();
    } catch( e ) {
        console.log(e);
    }
    _reattachElement( $table, origTablePosition );
    $( '#interactive-map .markupmap' ).eq( 0 ).on( {
        mousedown: _mapDown,
        mouseup: _mapUp
    } ).closest( '.zoomablemap' ).on( {
        mapclick: _zoomableMapClick
    } );
},
/*
    * Remove and element from the DOM without deleting it (ie. detach it), and
    * returns its former position (either its previous sibiling, if any, or its
    * parent)
*/
_detachElement = function( $el ) {
    var $prev, $parent;
    $prev = $el.prev();
    $parent = $el.parent();
    $el.detach();
    if ( $prev.length > 0 ) {
        return [ $prev, null ];
    }
    return [ null, $parent ];
},
/*
    * Reattach an element detached with _detachElement
*/
_reattachElement = function( $el, origPosition ) {
    if ( origPosition[0] ) {
        $el.insertAfter( origPosition[0] );
    } else {
        $el.prependTo( origPosition[1] );
    }
},
/*
    * Handler for changed selected checkbox. It determines wheter it's a "detail"
    * or "map" checkbox, and dispatches the right sub-handler (right below).
*/
_onChange = function( e ) {
    var $target = $( e.target ), $label = $target.closest( 'label' ), checked = $target.is( ':checked' ), $tr;
    $tr = $target.closest( 'tr' );
    if ( $label.hasClass( 'det' ) ) {
        _detailsChange( $tr, checked );
    } else if ( $label.hasClass( 'map' ) ) {
        _mapChange( $tr, $target, checked );
    }
},
/*
    * Handler for a change in the selected "details" checkbox
*/
_detailsChange = function( $tr, checked ) {
    var pp, fn, $prev;
    fn = checked ? 'show' : 'hide';
    while ( 1 ) {
        $tr = $tr.next();
        pp = $tr.data( 'principal' );
        if ( $tr.length === 0 || pp === 1 ) {
            break;
        }
        if ( typeof( pp ) == 'undefined' ) {
            continue;
        }
        $tr[fn]();
    }
},
/*
    * Handler for a change in the selected "map" checkbox
*/
_mapChange = function( $tr, $target, checked ) {
    let recomputeList = false;
    let $table = $tr.closest( 'table' );
    // lch is the list of checked labels
    let $lch = $table.find( 'label.map > input:checked' );
    // If a point were selected, makes all the rows visible according to
    // checkboxes again before applying the change
    if ( _selectedPoint ) {
        _reapplyTableRowVisibility( $table );
        _selectedPoint = null;
    }
    // The user has unchecked the checkbox
    if ( !checked ) {
        if ( _selectedPoint || $tr.data( 'principal' ) === 1 ) {
            // If there's a selected point, or it's the group heading, uncheck all
            $lch.each( function() {
                this.checked = false;
            } );
            $lch = $();
        }
        else {
            // If it's the detail row, unckeck the group heading
            let $tr2 = $tr;
            while ( 1 ) {
                $tr2 = $tr2.prev();
                if ( $tr2.length === 0 ) {
                    break;
                }
                if ( $tr2.data( 'principal' ) === 1 ) {
                    let $input2 = $tr2.find( 'label.map > input' );
                    if ( $input2[0].checked ) {
                        $input2[0].checked = false;
                        recomputeList = true;
                    }
                    break;
                }
            }
        }
    }
    // The user has checked the checkbox
    else {
        if ( $tr.data( 'principal' ) === 1 ) {
            // If it's the group heading, mark all from the group, but first unmark everything
            $lch.each( function() {
                this.checked = false;
            } );
            let $tr2 = $tr;
            while ( 1 ) {
                $tr2 = $tr2.next();
                if ( $tr2.length === 0 || $tr2.data( 'principal' ) === 1 ) {
                    break;
                }
                let $input2 = $tr2.find( 'label.map > input' );
                $input2[0].checked = true;
                recomputeList = true;
            }
            $target[0].checked = true;
        } else {
            // Not principal checkes
            if ( $lch.length > 1 ) {
                // Check a *different* row
                let $tr2;
                if ( $lch[0] === $target[0] ) {
                    $tr2 = $lch.eq( 1 ).closest( 'tr' );
                } else {
                    $tr2 = $lch.eq( 0 ).closest( 'tr' );
                }
                while ( 1 ) {
                    if ( $tr2.length === 0 || $tr2.data( 'principal' ) === 1 ) {
                        break;
                    }
                    $tr2 = $tr2.prev();
                }
                // Check against our row
                let $tr3 = $tr;
                while ( 1 ) {
                    if ( $tr3.length === 0 || $tr3.data( 'principal' ) === 1 ) {
                        break;
                    }
                    $tr3 = $tr3.prev();
                }
                if ( $tr3.length === 0 || $tr2.length === 0 || $tr3[0] !== $tr2[0] ) {
                    // Different group. Unmark all
                    $lch.each( function() {
                        this.checked = false;
                    } );
                    $lch = $target.prop( 'checked', true );
                }
            }
        }
    }
    if ( recomputeList ) {
        $lch = $tr.closest( 'table' ).find( 'label.map > input:checked' );
    }
    let points = $lch.map( function() {
        const ids = $( this ).closest( 'tr' ).data( 'ids' );
        if ( ids ) {
            return ids.split( ',' );
        }
    } ).get();
    _generateMap( points );
},
/*
    * Parse the field data-pointdata of a table row. Adds the elements to _mapData
*/
_parsePointData = function( str ) {
    var coords = str.split( ',' ), ret;
    ret = $.map( coords, function( coord ) {
        var parts, x, y, id, el;
        parts = coord.split( ':' );
        x = parseFloat( parts[1] );
        y = parseFloat( parts[2] );
        id = _createIdForCoords( x, y );
        el = { id: id, t: parts[0], x: x, y: y };
        _mapData[ id ] = el;
        return el;
    } );
    return ret;
},
_createIdForCoords = function( x, y ) {
    var xx, yy, bkey, seq, id, point;
    xx = String.fromCharCode( 65 + Math.trunc( x * 26 / _originalWidth ) );
    yy = String( Math.trunc( y * 26 / _originalHeight ) + 1 );
    bkey = xx + yy;
    if ( ! ( bkey in _buckets ) ) {
        _buckets[ bkey ] = [];
    }
    seq = _buckets[ bkey ].length;
    for ( var i = 0; i < seq; i++ ) {
        id = _buckets[ bkey ][ i ];
        point = _mapData[ id ];
        if ( point.x == x && point.y == y ) {
            return id;
        }
    }
    id = bkey + String.fromCharCode( 97 + seq );
    _buckets[ bkey ].push( id );
    return id;
},
/*
    * Generate the overlay map with markers that is displayed over the "base" map
    * image. If filterIds is an array, only shows markers corresponding to ids in
    * filterIds.
    *
    * If I got it right, this thing creates an image and assign it a temporary
    * URL, then loads it in the page as if it were a normal image.
*/
_generateMap = function( filterIds ) {
    var svgData, blob;
    svgData = '<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:w="https://schemas.wikidex.net/svg/generic" width="2048" height="2048" viewBox="0 0 2048 2048" version="1.1"><defs><filter id="invertColor" color-interpolation-filters="sRGB"><feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" /></filter><g id="m_estrella" transform="scale(0.6)"><path d="M -0.00164411,-27.941229 6.5673111,-9.0431814 26.570342,-8.63555 10.627149,3.4517155 16.420745,22.601693 -0.00164481,11.173986 -16.424036,22.601692 -10.630439,3.4517155 -26.573631,-8.6355515 -6.5706009,-9.0431814 Z" style="fill:#fcf7db;stroke:#000000;stroke-width:2" /></g><g id="m_distorsion" transform="scale(0.6)"><path d="m 10.898436,-19.664347 c -0.677587,-0.0649 -1.573603,0.42932 -2.251952,0.66016 -1.51606,0.5159 -3.107422,1.29207 -5.808594,1.26367 -2.25520899,-0.0237 -4.683473,-0.51286 -6.755859,0.37695 -2.257217,0.96917 -4.243107,2.86657 -5.367188,5.05078 -1.360569,2.64373 -1.032942,5.87122 -1.199219,8.83985 -0.02794,0.49879 -0.154571,1.08489 -0.294922,1.6875 -0.207206,-0.15406 -0.405503,-0.306 -0.564453,-0.45313 -0.85685,-1.14842 -0.646419,-2.92869 -1.261719,-4.29297 -0.69805,-1.54775 -2.319055,-3.10109 -4.546875,-4.41992 -0.65018,-0.38489 -1.650675,-1.01298 -2.208985,-0.5039 -0.70597,0.64374 0.07113,1.93537 0.378907,2.83984 0.5159,1.51606 1.290118,3.10741 1.261719,5.80859 -0.02371,2.2551999 -0.510904,4.68347 0.378906,6.75586 0.96917,2.25722 2.866571,4.2431 5.050781,5.36719 2.643731,1.36056 5.871215,1.03295 8.839845,1.19922 0.49827,0.0279 1.083594,0.15474 1.685547,0.29492 -0.153504,0.20634 -0.304551,0.40605 -0.451172,0.56445 -1.148414,0.85685 -2.928688,0.64642 -4.292969,1.26172 -1.547744,0.69805 -3.101094,2.31906 -4.419923,4.54688 -0.384895,0.65018 -1.014948,1.65067 -0.505859,2.20898 0.643733,0.70597 1.937333,-0.0711 2.841798,-0.3789 1.51606,-0.5159 3.107421,-1.29012 5.808593,-1.26172 2.25520901,0.0237 4.683474,0.5109 6.75586,-0.37891 2.257217,-0.96917 4.241153,-2.86657 5.365234,-5.05078 1.360567,-2.6437298 1.032941,-5.87121 1.199218,-8.83984 0.02791,-0.49827 0.15669,-1.0836 0.296875,-1.68555 0.20625,0.15345 0.404152,0.3046 0.5625,0.45117 0.85685,1.14842 0.648372,2.92869 1.263672,4.29297 0.69805,1.54775 2.317102,3.1010902 4.544922,4.41992 0.65018,0.38489 1.652627,1.01299 2.210937,0.50391 0.70597,-0.64374 -0.07113,-1.9353798 -0.378906,-2.83985 -0.5159,-1.51606 -1.292072,-3.10741 -1.263672,-5.80859 0.02371,-2.25520005 0.510904,-4.68347 -0.378906,-6.75586 -0.96917,-2.25722 -2.866572,-4.24114 -5.050781,-5.36523 -2.64373,-1.36056 -5.86926,-1.03491 -8.83789,-1.20118 -0.498789,-0.0279 -1.084888,-0.15457 -1.6875,-0.29492 0.153549,-0.20641 0.306462,-0.406 0.453125,-0.56445 1.148414,-0.85685 2.928688,-0.64642 4.292969,-1.26172 1.547744,-0.69805 3.101094,-2.31905 4.41992,-4.54687 0.384896,-0.65018 1.012996,-1.65068 0.503907,-2.20899 -0.160933,-0.17649 -0.362028,-0.25962 -0.587891,-0.28125 z M 0,-8.549107 A 8.5714283,8.5714283 0 0 1 8.570312,0.0212029 8.5714283,8.5714283 0 0 1 0,8.593463 8.5714283,8.5714283 0 0 1 -8.572266,0.0212029 8.5714283,8.5714283 0 0 1 0,-8.549107 Z" style="fill:#fcf7db;stroke:#000000;stroke-width:2" /></g><g id="m_arbol" transform="scale(0.6)"><path d="m 14.753887,-12.60332 c 2.321432,12.92478948 -20.7866602,27.5195 -35.289063,12.07030948 -0.11441,0.858 -0.175075,1.72232012 -0.18164,2.58789012 C -20.716942,13.52671 -11.417159,22.82649 0.05466773,22.82637 11.526496,22.82649 20.826279,13.52671 20.826153,2.0548796 c -0.0047,-5.49708 -2.188285,-10.76814 -6.072266,-14.6581996 z" style="fill:#fcf7db;stroke:#000000;stroke-width:2;" /><path d="m -0.19533227,-19.90606 c -0.99263503,0.0139 -2.00287793,0.10422 -3.01562493,0.26954 -0.463143,0.0783 -0.925867,0.17216 -1.386719,0.28125 -0.355533,2.09759 -2.33e-4,3.54672 0.643581,4.99609 l -1.294071,0.18821 c -1.723492,-2.38726 -2.139709,-5.31489 -2.147505,-8.18333 -2.7873118,-0.72381 -5.1244078,-0.40649 -6.9571998,1.04456 0.10172,4.35807 2.022044,7.58509 5.0637888,10.01114 l -0.9787828,1.0417 c -1.62928,-0.7875 -3.019008,-1.54784 -4.400123,-3.65889 -2.396701,2.42115 -3.717804,5.2353396 -3.720703,7.9257796 5.98e-4,6.17023988 6.795591,10.06483 15.1777338,8.69922 8.38303,-1.36564 15.1792642,-7.47543 15.1796882,-13.6464896 1.28e-4,-5.31558 -5.0869382,-9.06635 -12.16406327,-8.96875 z" style="fill:#fcf7db;stroke:#000000;stroke-width:2;" /></g><g id="m_caja" transform="scale(0.6)"><rect ry="2.5" y="1.0535715" x="-18.549109" height="12" width="37" style="fill:#fcf7db;stroke:#000000;stroke-width:2;" /><rect ry="2.5" y="-12.946428" x="-18.549109" height="20" width="37" style="fill:#fcf7db;stroke:#000000;stroke-width:2;" /><rect ry="1.2" y="6.0535722" x="-6.5491071" height="2.4000001" width="12" style="fill:#000000;stroke:#000000;stroke-width:2;" /><rect ry="1.8" y="-7.2464285" x="-23.099108" height="14.27" width="3.6500001" style="fill:#fcf7db;stroke:#000000;stroke-width:2;" /><rect style="fill:#fcf7db;stroke:#000000;stroke-width:2;" width="3.6500001" height="14.27" x="19.450891" y="-7.2464285" ry="1.8" /></g><g id="m_roca" transform="scale(0.6)"><path d="m -14.910709,22.571429 5.8035696,-5.71429 -11.2499996,-10.7142804 3.125,-8.83929 7.05357,2.49999996 6.4285691,12.14286044 1.25,-0.35714 -3.39285,-25.80358 6.24999987,-8.03571 H 4.0617301 l 8.3489909,8.30357 -4.9107209,25.53572 0.71429,0.53571 5.6250009,-6.5178604 6.42857,-1.42857 0.26786,6.5178604 -6.51786,6.25 4.53639,5.62503 z" style="fill:#fcf7db;stroke:#000000;stroke-width:2;" /></g></defs>';
    for ( var id in _mapData ) {
        if ( _mapData.hasOwnProperty( id ) ) {
            if ( !filterIds || filterIds.length == 0 || filterIds.indexOf( id ) != -1 ) {
                if ( id != _selectedPoint ) {
                    svgData += _pointToSvgEl( _mapData[ id ], false );
                }
            }
        }
    }
    if ( _selectedPoint ) {
        svgData += _pointToSvgEl( _mapData[ _selectedPoint ], true );
    }
    svgData += '</svg>';
    blob = new Blob( [ svgData ], { type : 'image/svg+xml' } );
    if ( _svg ) {
        URL.revokeObjectURL( _svg );
    }
    _svg = URL.createObjectURL( blob );
    $( '#interactive-map .markupmap' ).eq( 0 ).empty().append( $( '<img>' ).attr( {
        src: _svg,
        width: _originalWidth,
        height: _originalHeight
    } ) );
},
/*
    * Given a point (things stored in _mapData) return the svg code to display it.
*/
_pointToSvgEl = function( point, selected ) {
    var extra = '';
    if ( selected ) {
        extra = ' style="filter:url(#invertColor)"';
    }
    return '<use xlink:href="#' + _expandedPointTypes[ point.t ] + '" x="' + point.x + '" y="' + point.y + '" w:id="' + point.id + '"' + extra + '/>';
},
_mapDown = function( e ) {
    _downCoords = [ e.clientX, e.clientY ];
},
_mapUp = function( e ) {
    var upCoords;
    upCoords = [ e.clientX, e.clientY ];
    // Discard event if it's scrolling
    for ( var i = 0; i < 2; i++ ) {
        if ( Math.abs( upCoords[ i ] - _downCoords[ i ] ) > _sensibility ) {
            return;
        }
    }
    _downCoords = null;
    _mapUpFiltered.call( this, e );
    return false;
},
_zoomableMapClick = function( e ) {
    if ( _useMapUpFallback ) {
        $( '#interactive-map .markupmap' ).eq( 0 ).off( 'mousedown mouseup' );
        _useMapUpFallback = false;
    }
    _mapUpFiltered.call( this, e );
},
_mapUpFiltered = function( e ) {
    var upCoords, hit, points, idx, newSelection = null;
    upCoords = [ e.clientX, e.clientY ];
    hit = _translateToMap( upCoords );
    points = _getPointsInHit( hit );
    if ( points.length > 0 ) {
        if ( _selectedPoint && points.indexOf( _selectedPoint ) != -1 ) {
            // One of the target points is the one previously selected
            // Force changing the selection, or unselect
            if ( points.length > 1 ) {
                idx = points.indexOf( _selectedPoint );
                if ( idx == points.length - 1 ) {
                    idx = 0;
                } else {
                    idx++;
                }
                newSelection = points[ idx ];
            }
        } else {
            newSelection = points[ 0 ];
        }
    }
    if ( !_selectedPoint && !newSelection ) {
        return;
    }
    _selectedPoint = newSelection;
    _generateMap();
    _filterTableWithPoint();
},
_translateToMap = function( coords ) {
    var rect = $( '#interactive-map .markupmap img' )[ 0 ].getClientRects()[ 0 ], factor = 1;
    factor = ( rect.right - rect.left ) / _originalWidth;
    return [ ( coords[ 0 ] - rect.left ) / factor, ( coords[ 1 ] - rect.top ) / factor, factor ];
},
_getPointsInHit = function( hit ) {
    var sensibility = _sensibility / hit[ 2 ], points = [], c1, c2;
    for ( var id in _mapData ) {
        if ( _mapData.hasOwnProperty( id ) ) {
            c1 = Math.abs( _mapData[ id ].x - hit[ 0 ] );
            c2 = Math.abs( _mapData[ id ].y - hit[ 1 ] );
            if ( Math.sqrt( c1 * c1 + c2 * c2 ) - _pointRadius <= sensibility ) {
                points.push( id );
            }
        }
    }
    return points;
},
_filterTableWithPoint = function() {
    let $table = $( '#interactive-map-controller' ).eq( 0 )
    let origTablePosition = _detachElement( $table );
    try {
        // Uncheck all "map" checkboxes
        $table.find( 'label.map > input:checked' ).each( function() {
            this.checked = false;
        } );
        if ( _selectedPoint ) {
            // If one point is selected
            $table.find( '> tbody > tr[data-principal]' ).each( function( idx ) {
                let $tr = $( this );
                let ids = $tr.data( 'ids' );
                if ( !ids || ids.split( ',' ).indexOf( _selectedPoint ) === -1 ) {
                    $tr.hide();
                } else {
                    $tr.show();
                    $tr.find( 'label.map > input' )[ 0 ].checked = true;
                }
            } );
        }
        else {
            // If no point is selected, show the whole table
            _reapplyTableRowVisibility( $table );
        }
    } catch( e ) {
        console.log(e);
    }
    _reattachElement( $table, origTablePosition );
},
/*
    * Reapply the visibility of table cells detemined by checkboxes (this could
    * not be the case if a point was selected on the map)
*/
_reapplyTableRowVisibility = function( $table ) {
    $table.find( '> tbody > tr[data-principal="1"]' ).each( function() {
        var $tr = $( this ).show(), $prev, op;
        // $prev = $tr.prev();
        // if ( $prev.hasClass( 'sep' ) ) {
        //     $prev.show();
        // }
        op = $tr.find( 'label.det > input' )[0].checked ? 'show' : 'hide';
        // Hide secondary
        while ( 1 ) {
            $tr = $tr.next();
            if ( $tr.length === 0 || $tr.data( 'principal' ) !== 0 ) {
                break;
            }
            $tr[ op ]();
        }
    } );
};

$(_init);

})( $ );