PageSourceSearch

https://www.communecter.org/assets/4f6421d0/js/mapD3.js?v=1790224428379

js communecter.org collected 2026-09-24 09:23:15 UTC 83,438 bytes, 1,814 lines download raw bytes

1/*global L, define */
2// (function (window, document, undefined) {
3//     'use strict';
4
5    
6// }(window, document));
7/*global L */
8(function (window, document, undefined) {
9    'use strict';
10        /**
11            * SVG Icon
12            * @type {L.SVGIcon}
13            * @extends {L.Icon}
14        */
15        L.SVGIcon = L.Icon.extend({
16            options: {
17                /** @var {L.Point} */
18                iconSize: [24, 24],
19                /** @var {L.Point} */
20                iconAnchor: [12, 12],
21                /** @var {Function} */
22                drawIcon: null,
23                /** @var {string} */
24                className: 'leaflet-svg-icon'
25            },
26
27            /**
28             * @param {HTMLElement} icon
29             * @returns {HTMLSVGElement}
30             */
31            createIcon: function (icon) {
32                var size = L.point(this.options.iconSize);
33
34                if (!icon || (icon.tagName != 'SVG')) {
35                    icon = document.createElement('div');
36                }
37
38                // icon.width = size.x;
39                // icon.height = size.y;
40
41                this._setIconStyles(icon, 'svg');
42
43                return icon;
44            },
45
46            /**
47             * @param {HTMLElement} icon
48             * @returns {null}
49             */
50            createShadow: function (icon) {
51                return null;
52            },
53
54            /**
55             * @param {HTMLElement} icon
56             * @param {string} type
57             * @private
58             */
59            _setIconStyles: function (icon, type) {
60                if (typeof this.options.drawIcon == 'function') {
61                    this.options.drawIcon.apply(this, arguments);
62                }
63                L.Icon.prototype._setIconStyles.apply(this, arguments);
64                var size = L.point(this.options.iconSize);
65                var anchor = size.divideBy(2);
66                icon.style.marginLeft = (-anchor.x)+"px";
67                icon.style.marginTop = (-size.y)+"px";
68            }
69        });
70
71        /**
72         * SVG Icon factory
73         * @param {Object} options
74         * @returns {L.SVGIcon}
75         */
76        L.svgIcon = function (options) {
77            return new L.SVGIcon(options);
78        };
79
80        /**
81         * AMD compatibility
82         */
83        if ((typeof define == 'function') && define.amd) {
84            define(L.SVGIcon);
85        }
86    /**
87     * Piechart Icon
88     * @type {L.D3Icon}
89     * @extends {L.SVGIcon}
90     */
91    L.D3Icon = L.SVGIcon.extend({
92        options: {
93            iconSize: [48, 48],
94            iconAnchor: [24, 24],
95            popupAnchor: [24, 24],
96            className: 'leaflet-piechart-icon',
97            type: 'pie'
98        },
99
100        /**
101         * @param {HTMLSVGElement} icon
102         * @param {string} type
103         */
104        _setIconStyles: function (icon, type) {
105            var data = this.options.data;
106            var label = this.options.label;
107            var legendeActivated = this.options.legendeActivated;
108            var container = typeof this.options.container !== 'undefined' ? this.options.container : "#graphD3";
109            // set the color scale
110            var color = d3.scaleOrdinal()
111                .domain(this.options.legende)
112                .range(d3.schemeTableau10);
113            
114            var Tooltip = $(container).find(".tooltip-wrapper");
115    
116            var mouseover = function(event, d) {
117                d = typeof d.data !== "undefined" ? d.data : d;
118                Tooltip
119                    .css({
120                        "opacity":1,
121                        "z-index":9999,
122                    "left": $(this).offset().left+ 'px',
123                    "top": $(this).offset().top - $(container).offset().top + 'px'})
124                Tooltip.find("p")
125                    .html("Il y a "+d[1]+" organisation"+(d[1] > 1 ? "s" : "")+" de <span class='tooltip-label'>"+label+" "+d[0]+"</span>");
126                d3.select(this)
127                    .style("stroke", "black")
128            }
129
130            var mousemove = function(event, d) {
131                    // .style("left", (d3.mouse(this)[0]+70) + "px")
132                    // .style("top", (d3.mouse(this)[1]) + "px")
133            }
134            if ((type == 'svg') && data ) {
135                data = Object.fromEntries(Object.entries(data).filter(([key]) => legendeActivated.includes(key)))
136                if(this.options.type == 'donut') {
137
138                    var total = this._getTotal(data);
139                    var size = L.point(this.options.iconSize);
140                    var center = size.divideBy(2);
141
142                    // The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
143                    var radius = Math.min(center.x, center.y) - 2;
144                    // append the svg object to the div called 'my_dataviz'
145                    var svg = d3.select(icon)
146                        .attr('style', "position:absolute")
147                    .append("svg")
148                        .attr("width", size.x)
149                        .attr("height", size.y)
150                    .append("g")
151                        .attr("transform", "translate(" + center.x+ "," + center.y+ ")");
152
153                    // Compute the position of each group on the pie:
154                    const pie = d3.pie()
155                        .value(function(d) {return d[1]})
156                    const data_ready = pie(Object.entries(data))
157                    // Now I know that group A goes from 0 degrees to x degrees and s
157o on.
158
159                    // shape helper to build arcs:
160                    var arcGenerator = d3.arc()
161                        .innerRadius(radius * 0.5)
162                        .outerRadius(radius)
163                    // Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
164                    
165
166                    var mouseleave = function(event, d) {
167                        Tooltip
168                            .css({"opacity":0,"z-index": -999999})
169                    }
170                    svg
171                        .selectAll('mySlices')
172                        .data(data_ready)
173                        .join('path')
174                            .attr('d', arcGenerator)
175                            .attr('fill', function(d){ return(color(d.data[0] == '' ? "Autre" : d.data[0])) })
176                            .attr("stroke", "black")
177                            .style('pointer-events', 'inherit')
178                            .style("stroke-width", "2px")
179                            .style("opacity", 0.75)
180                            .on('mouseover', mouseover)                                                          // NEW
181                            .on('mouseleave', mouseleave)
182                }
183                else if(this.options.type == 'pie') {
184
185                    var total = this._getTotal(data);
186                    var size = L.point(this.options.iconSize);
187                    var center = size.divideBy(2);
188
189                    // The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
190                    var radius = Math.min(center.x, center.y) - 2;
191                    // append the svg object to the div called 'my_dataviz'
192                    var svg = d3.select(icon)
193                        .attr('style', "position:absolute")
194                        .append("svg")
195                            .attr("width", size.x)
196                            .attr("height", size.y)
197                        .append("g")
198                            .attr("transform", "translate(" + center.x+ "," + center.y+ ")");
199                        
200
201                    var mouseleave = function(event, d) {
202                        Tooltip
203                            .css({"opacity":0,"z-index": -999999})
204                    }
205                    // Compute the position of each group on the pie:
206                    const pie = d3.pie()
207                        .value(function(d) {return d[1]})
208                    const data_ready = pie(Object.entries(data))
209                    // Now I know that group A goes from 0 degrees to x degrees and so on.
210
211                    // shape helper to build arcs:
212                    var arcGenerator = d3.arc()
213                        .innerRadius(0)
214                        .outerRadius(radius)
215                    
216                    // Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
217                    var path = svg
218                        .selectAll('path')
219                        .data(data_ready)
220                        .join('path')
221                            .attr('d', arcGenerator)
222                            .attr('fill', function(d){ return(color(d.data[0] == '' ? "Autre" : d.data[0])) })
223                            .attr("stroke", "black")
224                            .style('pointer-events', 'inherit')
225                            .style("stroke-width", "1px")
226                            .style("opacity", 0.75)
227                            // .on("mouseover", function (event, d) {
228                            //     alert("data from event"+event.target)
229                            // })
230                        // .append("title")
231                        //     .text(d => d.data[0]+" : " + d.data[1]);
232                    path.on('mouseover', mouseover);                                                           // NEW
233                    path.on('mouseleave', mouseleave);
234                    svg
235                        .selectAll('mySlices')
236                        .data(data_ready)
237                        .join('text')
238                        .text(function(d){return (d.data[1]).toFixed(0)})
239                        .attr("transform", function(d) { return `translate(${arcGenerator.centroid(d)})`})
240                        .style("text-anchor", "middle")
241                        .style("font-size", 10)
242                        .style("color", function(d){ return(color(d.data[0] == '' ? "Autre" : d.data[0]))})
243                        // .on("mouseover", mouseover)
244                        // .on("mousemove", mousemove)
245                        // .on("mouseleave", mouseleave)
246                }
247                else if(this.options.type == 'bar') {
248                    var size = L.point(this.options.iconSize);
249                    var center = size.divideBy(2);
250
251                    // The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
252                    var radius = Math.min(center.x, center.y) - 2;
253                    // append the svg object to the div called 'my_dataviz'
254                    var svg = d3.select(icon)
255                        .attr('style', "position:absolute")
256                        .append("svg")
257                            .attr("width", size.x)
258                            .attr("height", size.y)
259                        .append("g")
260                            .attr("transform",
261                                "translate(-5,0)");
262
263
264                    var mouseleave = function(event, d) {
265                        Tooltip
266                            .css({"opacity":0,"z-index": -999999})
267                        d3.select(this)
268                            .style("stroke", "none")
269                    }
270                        // Add Y axis
271                    const x = d3.scaleBand()
272                        .range([ 0, size.x ])
273                        .domain(Object.keys(data))
274                        .padding(0.2);
275                    svg.append("g")
276                        .attr("transform", `translate(0,${size.y})`)
277                        .call(d3.axisBottom(x))
278                        // Add Y axis
279                    const y = d3.scaleLinear()
280                        .domain([0, Math.max(...Object.values(data))])
281                        .range([ size.y, 0]);
282                    svg.append("g")
283                        .attr("class", "myYaxis")
284                        .call(d3.axisLeft(y));
285                    var u = svg.selectAll("rect")
286                        .data(Object.entries(data));
287                    u
288                        .join("rect")
289                        .on("mouseover", mouseover)
290                        .on("mousemove", mousemove)
291                        .on("mouseleave", mouseleave)
292                        .transition()
293                        .duration(1000)
294                        .attr("x", d => x(d[0]))
295                        .attr("y", d => y(d[1]))
296                        .attr("width", x.bandwidth())
297                        .attr("height", d => size.y - y(d[1]))
298                        .attr("fill", function(d){ return(color(d[0])) })
299                }
300            }
301
302            L.SVGIcon.prototype._setIconStyles.apply(this, arguments);
303        },
304
305        /**
306         * @param {Object[]} data Array of objects
307         * @param {string} field Field to summarize
308         * @returns {number}
309         * @private
310         */
311        _getTotal: function (data) {
312            var total = 0;
313            for(const [key, value] of Object.entries(data)) {
314                total += value;
315            }
316            return total;
317        },
318    });
319
320    /**
321     * Pie char Icon factory
322     * @param {Object} options
323     * @returns {L.D3Icon}
324     */
325    L.d3Icon = function (options) {
326        return new L.D3Icon(options);
327    };
328
329    /**
330     * Pie chart Marker
331     * @type {L.Marker}
332     */
333    L.D3Marker = L.Marker.extend({
334        options: {
335            type: 'pie',
336            icon: null,
337            radius: 20,
338            riseOnHover: true
339        },
340
341        initialize: function (latlng, options) {
342            var opts = {};
343            L.Util.extend(opts, options);
344            if (opts.radius) {
345                var diameter = opts.radius * 2;
346                opts.iconSize = [diameter, diameter];
347                opts.iconAnchor = [opts.radius, opts.radius];
348            }
349            opts.icon = L.d3Icon(opts);
350            L.Marker.prototype.initialize.apply(this, [latlng, opts]);
351        }
352    });
353
354    /**
355     * Pie chart Marker factory
356     * @param {L.LatLng} latlng
357     * @param {Object} options
358     * @returns {L.D3Marker}
359     */
360    L.d3Marker = function (latlng, options) {
361        return new L.D3Marker(latlng, options);
362    }
363}(window, document));
364
365var unique = function(array) {
366    var a = array;
367    for(var i=0; i<a.length; ++i) {
368        for(var j=i+1; j<a.length; ++j) {
369            if(a[i] === a[j])
370                a.splice(j--, 1);
371        }
372    }
373
374    return a;
375};
376
377function MapD3(options=null){
378    /* private properties ==========> */
379    var _context = this;
380    var _map = null;
381    var _options = {
382        parentContainer: '#mapContainer',
383        parentContainerName: 'mapContainer',
384        container: '#mapContainerMap',
385        hideContainer: 'search-content',
386        data: {},
387        colors: d3.scaleOrdinal()
388                .range(d3.schemeTableau10),
389        arrayBounds: [],
390        bounds: null,
391        legende: [],
392        legendeActivated: [],
393        lastLegende: [],
394        legendeVar: "tags",
395        legendeLabel: "type",
396        clickedLegende: "",
397        showLegende: true,
398        dynamicLegende: true,
399        groupBy: "type",
400        markerType: 'default',
401        markersCluster: null,
402        geoJSONlayer: null,
403        markerList: {},
404        clusterType: 'pie',
405        activeCluster: true,
406        activePopUp: false,
407        activePreview: false,
408        listItem: true,
409        distanceToMax: 0,
410        elts:null,
411        mapOpt:{
412            dragging: true,
413            draggable: false,
414            center: [0, 0],
415            centerOneZoom:18,
416            zoom: 10,
417            offset: {
418                x: 0,
419                y: 0
420            },
421            menuRight: null,
422            btnHide: false,
423            minZoom: 0,
424            maxZoom: 18,
425            mouseOver : false,
426            doubleClick : false,
427            onclickMarker: null,
428        },
429        mapCustom:{
430            tile:"mapnik",
431            css:[],
432            icons: {
433                pie: modules.map.assets + '/images/icons/pie.png',
434                bar: modules.map.assets + '/images/icons/bar.png',
435                donut: modules.map.assets + '/images/icons/donut.png',
436                heatmap: modules.map.assets + '/images/icons/heatmap.png'
437            },
438            markers: {
439                default: modules.map.assets + '/images/markers/citizen-marker-default.png',
440                organization: modules.map.assets + '/images/markers/ngo-marker-default.png',
441                classified: modules.map.assets + '/images/markers/classified-marker-default.png',
442                proposal: modules.map.assets + '/images/markers/proposal-marker-default.png',
443                poi: modules.map.assets + '/images/markers/poi-marker-default.png',
444                project: modules.map.assets + '/images/markers/project-marker-default.png',
445                event: modules.map.assets + '/images/markers/event-marker-default.png',
446                answer: modules.map.assets + '/images/markers/services/tools-hardware.png',
447                getMarker: function(data){
448                    var markers = _options.mapCustom.markers;
449                    var collectionGroups = {
450                        organization: ["organization", "organizations", "NGO", "Cooperative"],
451                        classified: ["classified", "classifieds"],
452                        proposal: ["proposal", "proposals"],
453                        poi: ["poi"],
454                        project: ["project", "projects"],
455                        event: ["event", "events"],
456                        answer: ["answers"]
457                    }
458                    var marker = markers.default;
459
460                    if(data){
461                        if(data.profilMarkerImageUrl)
462                            marker = data.profilMarkerImageUrl;
463                        else if(data.collection){
464                            $.each(collectionGroups, function(k, v){
465                                if(v.indexOf(data.collection) > -1){
466                                    marker = markers[k]
467                                }
468                            })
469                        }
470                    }
471
472                    return marker;
473                }
474            },
475            icon:{
476                options:{
477                    iconSize: [45, 55],
478                    iconAnchor: [25, 45],
479                    popupAnchor: [-3, -30]
480                },
481                getIcon:function(params){
482                    if(_options.markerType == "default"){
483                        var iconOptions = _options.mapCustom.icon.options;
484                        iconOptions.data = params.elt;
485                        iconOptions.iconUrl = _options.mapCustom.markers.getMarker(params.elt);
486                        return L.icon(iconOptions);
487                    }else{
488                        var iconOptions = _options.mapCustom.icon.options;
489                        iconOptions.data = params.elt;
490                        iconOptions.type = _options.markerType;
491                        iconOptions.legende = params.legende;
492                        iconOptions.legendeActivated = params.legendeActivated;
493                        iconOptions.label = _options.legendeLabel;
494                        // iconOptions.iconUrl = _options.mapCustom.markers.getMarker(params.elt);
495                        return L.d3Icon(iconOptions);
496                    }
497                }
498            },
499            lists: {
500                text:"",
501                createItemList: function(kElt, element, container){
502                    if(element.type && $.inArray(element.type, _filtres.types) == -1){
503                        _filtres.types.push(element.type)
504                        var valueIdType = element.type.replace(/[^A-Za-z0-9]/g, "");
505                        if (!$('#item_panel_map_' + valueIdType).length){
506                            var newItem = "<button class='item_panel_map' id='item_panel_map_" + valueIdType + "'>" +
507                                        "<i class='fa fa-tag' ></i> " + element.type +
508                                        "</button>";
509                            $(_options.parentContainer +' .panel_filter').append(newItem);
510
511                            $("#item_panel_map_" + valueIdType).click(function(){
512                                $(".item_panel_map").removeClass("active");
513                                $("#item_panel_map_" + valueIdType).addClass("active");
514                                _filtres.typesActived = []
515                                if($.inArray(valueIdType, _filtres.typesActived) == -1){
516                                    _filtres.typesActived.push(valueIdType)
517                                }
518                                _context.clearMap()
519                                _context.addElts(_options.data)
520                            })
521                        }
522                    }
523                    //prépare le nom (ou "anonyme")
524                    var name = (element.name)?element.name:(element.title?element.title:"Anonyme")
525                    //recuperation de l'image de profil (ou image par defaut)
526                    var imgProfilPath = _options.mapCustom.getThumbProfil(element)
527                    var button = '<div class="element-right-list" id="element-right-list-' + kElt + '">' +
528                                '<button class="item_map_list item_map_list_' + kElt + '">' +
529                                "<div class='left-col'>" +
530                                "<div class='thumbnail-profil'><img class='lzy_img' height=50 width=50 src='" + modules.co2.url +"/images/thumbnail-default.jpg' data-src='" + imgProfilPath + "'></div>" +
531                                "</div>" +
532                                "<div class='right-col'>" +
533                                "<div class='info_item pseudo_item_map_list'>" + name + "</div>";
534                    if(element.tags){
535                        button += "<div class='info_item items_map_list'>";
536                        var totalTags = 0;
537                        $.each(element.tags, function(index, value){
538                            totalTags++;
539                            if(totalTags < 4){
540                                var t = tradCategory[value]?tradCategory[value]:value;
541                                button += "<a href='javascript:' class='tag_item_map_list'>#" + t + " </a>";
542                            }
543                            var valueId = value.replace(/[^A-Za-z0-9]/g, "");
544                            //si l'item n'existe pas deja
545                            if (!$('#item_panel_map_' + valueId).length){
546                                var newItem = "<button class='item_panel_map' id='item_panel_map_" + valueId + "'>" +
547								                "<i class='fa fa-tag' ></i> " + value +
548                                            "</button>";
549                                $(_options.parentContainer + ' .panel_map2').append(newItem);
550
551                                $("#item_panel_map_"+valueId).click(function(){
552                                    $(".item_panel_map").removeClass("active");
553                                    $("#item_panel_map_" + valueId).addClass("active");
554                                    
555                                    _filtres.tagsActived = [];
556                                    if($.inArray(value, _filtres.tagsActived) == -1){
557                                        _filtres.tagsActived.push(value)
558                                    }
559                                    _context.clearMap()
560                                    _context.addElts(_options.data)
561                                })
562                            }
563                            if($.inArray(value, _filtres.tags) == -1)
564                                _filtres.tags.push(value)
565                        })
566                        button += "</div>";
567                    }
568
569                    if(element.address){
570                        button += "<div class='info_item city_item_map_list inline'>";
571                        if(element.address.addressLocality){
572                            button += element.address.addressLocality + " "
573                        }
574                        if(element.address.postalCode){
575                            button += element.address.postalCode + " "
576                        }
577                        if(element.address.addressCountry){
578                            button += element.address.addressCountry + " "
579                        }
580                        button += "</div>";
581                    }
582                    button += "</div>";
583
584                    if(element.type == "event" || element.type == "events"){
585                        button += displayStartAndEndDate(element);
586                    }
587                    button += '<div class="col-xs-12 separation"></div>' +
588                                '</button>' +
589                            '</div>';
590                    $(_options.parentContainer + " .liste_map_element").append(button)
591                
592                    $(_options.parentContainer + " .item_map_list_" + kElt).click(function(){
593                        if(element.geo && element.geo.latitude && element.geo.longitude){
594                            var m = _options.markerList[kElt]
595                            _options.markersCluster.zoomToShowLayer(m, function(){
596                                m.openPopup()
597                                coInterface.bindLBHLinks();
598                            })
599                        }else{
600                            $(_options.parentContainer + " .modalItemNotLocated").modal('show');
601                            $(_options.parentContainer + " .modalItemNotLocated .modal-body").html("<i class='fa fa-spin fa-reload'></i>");
602                            $(_options.parentContainer + " .modalItemNotLocated .modal-body").html(_options.mapCustom.getPopup(element))
603                            $(_options.parentContainer + " .modalItemNotLocated #btn-open-details").click(function(){
604                                $("#popup" + kElt).click();
605                                if(networkJson && networkJson.dataSrc){
606                                    urlCtrl.loadByHash("'+url+'");
607                                }
608                            })
609
610                            $(_options.parentContainer + " .modalItemNotLocated #popup" + kElt).click(function(){
611                                $(_options.parentContainer + " .modalItemNotLocated").modal('hide')
612                            })
613                        }
614                    })
615                }
616            },
617            transformData: function(allData, type) {
618                if(_options.dynamicLegende == "false"  || _options.dynamicLegende == false){
619                    if(type.data){
620                        for (const [key, legende] of Object.entries(_options.legende)) {
621                            if(type[_options.legendeVar].includes(legende)){
622                                allData[legende] = allData[legende] || 0;
623                                allData[legende] += type.data[legende];
624                            }
625                        }
626                    }else{
627                        var existe = false;
628                        if(typeof type[_options.legendeVar] != "undefined"){
629                            if(type[_options.legendeVar] instanceof Array || type[_options.legendeVar] instanceof Object){
630                                for (const [key, legende] of Object.entries(_options.legende)) {
631                                    if(type[_options.legendeVar].includes(legende) && legende != "Autre"){
632                                        allData[legende] = allData[legende] || 0;
633                                        allData[legende] += 1;
634                                        existe = true;
635                                    }
636                                    if(key == _options.legende.length - 1){
637                                        if(!existe){
638                                            allData["Autre"] = allData["Autre"] || 0;
639                                            allData["Autre"] += 1;
640                                        }
641                                    }
642                                }
643                            }
644                            
645                        }else{
646                            allData["Autre"] = allData["Autre"] || 0;
647                            allData["Autre"] += 1;
648                        }
649                    }
650                }else if(type.data){
651                    for (const [key, value] of Object.entries(type.data)) {
652                        allData[key] = allData[key] || 0;
653                        allData[key] += value;
654                    }
655                }else {
656                    if(type[_options.groupBy] && type[_options.groupBy] != ""){
657                        allData[type[_options.groupBy]] = allData[type[_options.groupBy]] || 0;
658                        allData[type[_options.groupBy]] += 1;
659                    }else{
660                        allData["Autre"] = allData["Autre"] || 0;
661                        allData["Autre"] += 1;
662                    }
663                }
664                return allData;
665            },
666            getData: function(allData, cluster){
667                if(cluster._markers){
668                    cluster._markers.forEach(element => {
669                        if(_options.mapCustom.transformDataCustom){
670                            allData = _options.mapCustom.transformDataCustom(allData, element.options.icon.options.data, _context)
671                        }else{
672                            allData = _options.mapCustom.transformData(allData, element.options.icon.options.data)
673                        }
674                    });
675        
676                }
677                if(cluster._childClusters.length > 0){
678                    cluster._childClusters.forEach(clusterChild => {
679                        _options.mapCustom.getData(allData, clusterChild);
680                    })
681                }
682            },
683            getClusterIcon: function(cluster){
684                var lastdata = [];
685                var width = 76;
686                _options.mapCustom.getData(lastdata,cluster);
687                if(_options.clickedLegende.length < 1){
688                    return L.d3Icon({type: _options.clusterType, legende: _options.legende,label: _options.legendeLabel, legendeActivated: _options.legendeActivated, data: lastdata, iconSize: [width, width]});
689                }else{
690                    if(typeof paramsMapCO.mapCustom.getClusterIcon(cluster) != "undefined")
691                        return paramsMapCO.mapCustom.getClusterIcon(cluster);
692                    var childCount = cluster.getChildCount();
693                    var c = ' marker-cluster-';
694                    if (childCount < 10) {
695                        c += 'small';
696                    } else if (childCount < 100) {
697                        c += 'medium';
698                    } else {
699                        c += 'large';
700                    }
701                    return L.divIcon({ html: '<div>' + childCount + '</div>', className: 'marker-cluster' + c, iconSize: new L.Point(40, 40) });
702                    
703                }
704            },
705            getThumbProfil: function (data) {
706                var imgProfilPath = modules.map.assets + "/images/thumb/default.png";
707                if (typeof data.profilThumbImageUrl !== "undefined" && data.profilThumbImageUrl != "")
708                    imgProfilPath = baseUrl + data.profilThumbImageUrl;
709                else
710                    imgProfilPath = modules.map.assets + "/images/thumb/default_" + data.collection + ".png";
711    
712                return imgProfilPath;
713            },
714            getPopup: function(data){
715                var id = data._id ? data._id.$id:data.id;
716                var imgProfil = _options.mapCustom.getThumbProfil(data)
717
718                var eltName = data.title ? data.title: ((typeof data.properties != "undefined" && typeof data.properties.name != "undefined") ? data.properties.name : data.name);
719                var popup = "";
720                popup += "<div class='padding-5' id='popup" + id + "'>";
721                popup += "<img src='" + imgProfil + "' height='30' width='30' class='' style='display: inline; vertical-align: middle; border-radius:100%;'>";
722                popup += "<span style='margin-left : 5px; font-size:18px'>" + eltName + "</span>";
723
724                if(data.tags && data.tags.length > 0){
725				    popup += "<div style='margin-top : 5px;'>";
726                    var totalTags = 0;
727                    $.each(data.tags, function(index, value){
728                        totalTags++;
729                        if (totalTags < 3) {
730                            popup += "<div class='popup-tags'>#" + value + " </div>";
731                        }
732                    })
733                    popup += "</div>";
734                }
735                if(data.address){
736                    var addressStr="";
737                    if(data.address.streetAddress)
738                        addressStr += data.address.streetAddress;
739                    if(data.address.postalCode)
740                        addressStr += ((addressStr != "")?", ":"") + data.address.postalCode;
741                    if(data.address.addressLocality)
742                        addressStr += ((addressStr != "")?", ":"") + data.address.addressLocality;
743                    popup += "<div class='popup-address text-dark'>";
744                    popup += 	"<i class='fa fa-map-marker'></i> "+addressStr;
745                    popup += "</div>";
746                }
747                if(data.shortDescription && data.shortDescription != ""){
748                    popup += "<div class='popup-section'>";
749					popup += "<div class='popup-subtitle'>Description</div>";
750					popup += "<div class='popup-shortDescription'>" + data.shortDescription + "</div>";
751				    popup += "</div>";
752                }
753                if((data.url && typeof data.url == "string") || data.email || data.telep
753hone){
754                    popup += "<div id='pop-contacts' class='popup-section'>";
755                    popup += "<div class='popup-subtitle'>Contacts</div>";
756                    
757                    if(data.url && typeof data.url === "string"){
758                        popup += "<div class='popup-info-profil'>";
759                        popup += "<i class='fa fa fa-desktop fa_url'></i> ";
760                        popup += "<a href='" + data.url + "' target='_blank'>" + data.url + "</a>";
761                        popup += "</div>";
762                    }
763
764                    if(data.email){
765                        popup += "<div class='popup-info-profil'>";
766                        popup += "<i class='fa fa-envelope fa_email'></i> " + data.email;
767                        popup += "</div>";
768                    }
769
770                    if(data.telephone){
771                        popup += "<div class='popup-info-profil'>";
772                        popup += "<i class='fa fa-phone fa_phone'></i> ";
773                        var tel = ["fixe", "mobile"];
774                        var iT = 0;
775                        $.each(tel, function(keyT, valT){
776                            if(data.telephone[valT]){
777                                $.each(data.telephone[valT], function(keyN, valN){
778                                    if(iT > 0)
779                                        popup += ", ";
780                                    popup += valN;
781                                    iT++; 
782                                })
783                            }
784                        })
785                        popup += "</div>";
786                    }
787
788                    popup += "</div>";
789				    popup += "</div>";
790                }
791                if(data.collection && id){
792                    var url = '#page.type.' + data.collection + '.id.' + id;
793                    popup += "<div class='popup-section'>";
794                    if(_options.activePreview)
795                        popup += "<a href='" + url + "' class='lbh-preview-element item_map_list popup-marker' id='popup" + id + "'>";
796                    else
797                        popup += "<a href='" + url + "' target='_blank' class='lbh item_map_list popup-marker' id='popup" + id + "'>";
798                    popup += '<div class="btn btn-sm btn-more col-md-12">';
799                    popup += '<i class="fa fa-hand-pointer-o"></i>' + trad.knowmore;
800                    popup += '</div></a>';
801                }
802                popup += '</div>';
803                popup += '</div>';
804
805                return popup;
806            },
807            addMarker:null,
808            addElts:null,
809            transformDataCustom : null
810        },
811        buttons:{
812            fullScreen:{
813                allow:false,
814                position: "bottomLeft",
815                icon:{
816                    maximize: 'expand',
817                    minimize: 'compress'
818                },
819                style:{
820                    default:{
821                        border: "none",
822                        borderRadius: "100%",
823                        width: "40px",
824                        height: "40px",
825                        background: "rgba(255,255,255,.5)",
826                        outline:"none"
827                    },
828                    onHover:{
829                        background: "rgba(255,255,255,1)"
830                    }
831                }
832            }
833        },
834        actionButtons:[]
835    }
836    var _utils = {
837        bindMap: function(){
838            $(_options.parentContainer + " .input_name_filter").keyup(function(){
839                if (notNull(_filtres.timeoutAddCity))
840                    clearTimeout(_filtres.timeoutAddCity);
841                
842                _filtres.timeoutAddCity = setTimeout(function () {
843					_filtres.search = $(_options.parentContainer + " .input_name_filter").val();
844                    _context.clearMap();
845                    _context.addElts(_options.data);
846				}, 500);
847            })
848
849            $(_options.parentContainer + " .item_panel_filter_all").off().click(function(){
850                $(_options.parentContainer + " .item_panel_map").removeClass("active");
851                $(_options.parentContainer + " .item_panel_filter_all").addClass("active");
852                _filtres.typesActived = [];
853                _context.clearMap();
854                _context.addElts(_options.data)
855            })
856
857            $(_options.parentContainer + " .item_panel_map_all").off().click(function(){
858                _filtres.tagsActived = [];
859                $(_options.parentContainer  + " .item_panel_map").removeClass("active")
860                $(_options.parentContainer + " .item_panel_map_all").addClass("active")
861                _context.clearMap();
862                _context.addElts(_options.data)
863            })
864
865            $(_options.parentContainer + " .btn-hide-map").off().click(function(){
866                showMap(false);
867            })
868
869            $(_options.parentContainer + " #btn-close-menuRight").off().click(function(){
870                if(!$(this).data("close")){
871                    $("#menuRight"+ _options.parentContainerName).hide();
872                    $(this).html('<i class="fa fa-angle-left"></i>');
873                    $(_options.parentContainer + " .closeMenuRight").css('right', "0px");
874                    $(this).data("close", true);
875                }else{
876                    $("#menuRight"+ _options.parentContainerName).show();
877                    $(this).html('<i class="fa fa-angle-right"></i>');
878                    var right = "341px";
879                    if(_options.mapOpt.menuRight && _options.mapOpt.menuRight.btnClose && _options.mapOpt.btnClose.right){
880                        right = _options.mapOpt.btnClose.right;
881                    }
882                    $(_options.parentContainer + " .closeMenuRight").css('right', right);
883                    $(this).data("close", false);
884                }
885            })
886        },
887        generateRandomId: function(){
888            return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(2, 10);
889        }
890    }
891    var _filtres = {
892        types: [],
893        typesActived: [],
894        tags: [],
895        tagsActived: [],
896        search: "",
897        result: function(elt){
898            var res = _filtres.isTags(elt) && _filtres.isSearch(elt) && _filtres.isType(elt)
899            return res;
900        },
901        isTags: function(elt){
902            var res = false;
903            if(Object.keys(_filtres.tagsActived).length == 0 || _filtres.tagsActived || _filtres.tagsActived.length == 0){
904                res = true;
905            }else{
906                $.each(_filtres.tagsActived, function(k, v){
907                    if(elt.tags && $.inArray(v, elt.tags) > -1)
908                        res = true;
909                })
910            }
911            return res;
912        },
913        isType: function(elt){
914            var res = false;
915            if(Object.keys(_filtres.typesActived).length == 0 || _filtres.typesActived || _filtres.typesActived.length == 0){
916                res = true
917            }else{
918                $.each(_filtres.typesActived, function(k, v){
919                    if(elt.type && v == elt.type)
920                        res = true;
921                })
922            }
923            return res;
924        },
925        isSearch: function(elt){
926            var res = false;
927            if(_filtres.search){
928                if(elt.name && elt.name.search(new RegExp(_filtres.search, "i")) >= 0)
929                    res = true;
930            }else{
931                res = true
932            }
933            return res;
934        }
935    }
936    /* <========== private properties */
937
938    /* private methodes ==========> */
939    var initVar = function(){
940        if(options){
941            _options = $.extend(true, {}, _options, options)
942
943            _options.parentContainer = (options.container) ? options.container:"#mapContainer";
944            _options.parentContainerName = _options.parentContainer.replace("#", "").replace(".","")
945            _options.container = _options.parentContainer + "Map";
946            _options.geoJSONlayer = new L.LayerGroup();
947            if(_options.activeCluster){
948                _options.markersCluster = new L.markerClusterGroup({
949                    iconCreateFunction: function (cluster) {
950                        return _options.mapCustom.getClusterIcon(cluster);
951                    }
952                })
953            }
954        }
955    }
956
957    var initView = function(){
958        var view = "";
959
960        view += "<div class='mapLoading'></div>";
961        view += ` <div class="tooltip-wrapper">
962            <div class="tooltip-content">
963                <p></p>
964            </div>
965        </div>`;
966        if(_options.mapOpt.menuRight){
967            var dataCloseMenuRight = false,
968                sizeList = $(_options.parentContainer).height() - 80;
969
970            if(_options.mapOpt.menuRight.btnClose){
971                view += '<div class="closeMenuRight">'+
972                            '<button type="button" data-close="'+dataCloseMenuRight+'" class="btn btn-default " id="btn-close-menuRight" data-toggle="dropdown">' +
973                                '<i class="fa fa-angle-right"></i>' +
974                            '</button>' +
975                        '</div>';
976            }
977
978            view += '<div id="menuRight' + _options.parentContainerName + '" class="hidden-xs col-sm-3 no-padding menuRight" style="">' +
979					'<div id="menuRight_search' + _options.parentContainerName + '" class="col-sm-12 no-padding menuRight_search">' +
980					'<div class="menuRight_header col-sm-12 padding-10">' +
981					'<div class="col-xs-6">' +
982					'<span id="result' + _options.parentContainerName + '" class=""></span>' +
983					'<span class="menuRight_header_title"> '+tradMap.Results+' </span>' +
984                '</div>';
985            
986            if(_options.mapOpt.btnHide){
987                view += '<div class="col-xs-2 pull-right">' +
988					'<button type="button" class="btn btnHeader dropdown-toggle btn-hide-map" data-toggle="dropdown">' +
989					'<i class="fa fa-times"></i>' +
990					'</button>' +
991					'</div>';
992            }
993            view += '<div class="col-xs-2 pull-right">' +
994				'<button type="button" class="btn btnHeader dropdown-toggle" id="btn-filters' + _options.parentContainerName + '" data-toggle="dropdown">' +
995				'<i class="fa fa-filter"></i>' +
996				'</button>' +
997				'<ul class="dropdown-menu panel_filter panel_map"  role="menu" aria-labelledby="panel_filter" style="overflow-y: auto; max-height: 400px;">' +
998				'<h3 class="title_panel_map"><i class="fa fa-angle-down"></i> '+tradMap['Filter results by types']+'</h3>' +
999				'<button class="item_panel_map active item_panel_filter_all" >' +
1000				'<i class="fa fa-star"></i> All' +
1001				'</button>' +
1002				'</ul>' +
1003				'</div>' +
1004				'<div class="col-xs-2 pull-right">' +
1005				'<button type="button" class="btn btnHeader dropdown-toggle" id="bt
1005n-panel' + _options.parentContainerName + '" data-toggle="dropdown">' +
1006				'<i class="fa fa-tags"></i>' +
1007				'</button>' +
1008				'<ul class="dropdown-menu panel_map panel_map2 pull-right" role="menu" aria-labelledby="panel_map" style="overflow-y: auto; max-height: 400px;">' +
1009				'<h3 class="title_panel_map"><i class="fa fa-angle-down"></i> '+tradMap['Filter results by tags']+'</h3>' +
1010				'<button class="item_panel_map active item_panel_map_all" >' +
1011				'<i class="fa fa-star"></i> All' +
1012				'</button>' +
1013				'</ul>' +
1014				'</div>' +
1015				'</div>' +
1016
1017
1018				'<div id="menuRight_search_filters' + _options.parentContainerName + '" class="col-sm-12 no-padding menuRight_search_filters">' +
1019				'<input class="form-control date-range active input_name_filter" type="text" placeholder="'+tradMap['Filter by names']+'">' +
1020				'</div>' +
1021				'<div class="menuRight_body col-sm-12 no-padding liste_map_element" ' +
1022				' style="height: ' + sizeList + 'px;"></div>' +
1023				'</div>' +
1024                '</div>';
1025            
1026            view += '<div class="modal fade modalItemNotLocated" role="dialog">' +
1027				'<div class="modal-dialog">' +
1028				'<div class="modal-content" style="top: 125px;">' +
1029				'<div class="modal-header">' +
1030				'<button type="button" class="close" data-dismiss="modal">&times;</button>' +
1031				'<h4 class="modal-title text-dark">' +
1032				'<i class="fa fa-map-marker"></i>' + trad["This data is not geolocated"] +
1033				'</h4>' +
1034				'</div>' +
1035				'<div class="modal-body"></div>' +
1036				'<div class="modal-footer" style="border-top: none;">' +
1037				'<button type="button" class="btn btn-default btn-sm btn-danger" data-dismiss="modal"><i class="fa fa-times"></i>' + trad.closed + '</button>' +
1038				'</div>' +
1039				'</div>' +
1040				'</div>' +
1041				'</div>';
1042        }else{
1043            if (_options.mapOpt.btnHide) {
1044				view += '<div class="col-xs-2 div-btn-hide">' +
1045					'<button type="button" class="btn btnHeader dropdown-toggle btn-hide-map" id="" data-toggle="dropdown" style="background-color: #e6434d; color: white;">' +
1046					'<i class="fa fa-times"></i>' +
1047					'</button>' +
1048					'</div>';
1049			}
1050        }
1051        // view += '<div id="' + _options.container + '" style="z-index: 1; width: 100%; position: fixed; bottom:0; top:inherit;"></div>';
1052        view += '<div id="' + _options.container + '" style="z-index: 1; width: 100%; height: 100%; position:relative;"></div>';
1053
1054        $(_options.parentContainer).html(view);
1055
1056        if(_options.mapOpt.menuRight && _options.mapOpt.menuRight.close){
1057            $("#menuRight"+ _options.parentContainerName).hide();
1058            $(_options.parentContainer + " #btn-close-menuRight").html('<i class="fa fa-angle-left"></i>');
1059            $(_options.parentContainer + " .closeMenuRight").css('right', "0px");
1060            $(_options.parentContainer + " #btn-close-menuRight").data("close", true);
1061        }
1062
1063        if(_options.buttons.fullScreen.allow)
1064            initBtnFullScreen();
1065        
1066        if(_options.actionButtons && Array.isArray(_options.actionButtons) && _options.actionButtons.length > 0){
1067            initBtnActions()
1068        }
1069    }
1070
1071    var initBtnFullScreen = function(){
1072        var optBtnFullScreen = _options.buttons.fullScreen,
1073            fullScreenActived = false,
1074            $parentContainer = $(_options.parentContainer),
1075            buttonContainerId = _options.parentContainerName + "-btn-fullscreen-container",
1076            defaultSizeParentContainer = {
1077                width: $parentContainer.width(),
1078                height: $parentContainer.height()
1079            }
1080
1081        var btnContainerPositionOptions = {
1082            topLeft: { left:0, top:0 },
1083            bottomLeft: { left:0, bottom:0 },
1084            topRight: { top:0, right:0 },
1085            bottomRight: { bottom:0, right:0 },
1086            default: { left:0, bottom:0 }
1087        }
1088        var btnContainerPosition = (btnContainerPositionOptions[optBtnFullScreen.position])? 
1089                                    btnContainerPositionOptions[optBtnFullScreen.position]:
1090                                    btnContainerPositionOptions.default;
1091        var btnContainerStyle = {
1092            position:"absolute",
1093            zIndex: 100000,
1094            padding:"20px"
1095        }
1096        $.each(btnContainerPosition, function(property, value){
1097            btnContainerStyle[property] = value;
1098        })
1099
1100        var button = `
1101            <div id="${buttonContainerId}">
1102                <button><i class="fa fa-${optBtnFullScreen.icon.maximize}" aria-hidden="true"></i></button>
1103            </div>
1104        `
1105        $parentContainer.append(button)
1106
1107        var $buttonContainerId = $("#"+buttonContainerId),
1108            $button = $buttonContainerId.find("button");
1109        
1110        $buttonContainerId.css(btnContainerStyle)
1111        $button.css(optBtnFullScreen.style.default)
1112        $button.hover(function(){
1113            $button.css(optBtnFullScreen.style.onHover)
1114        }, function(){
1115            $button.css(optBtnFullScreen.style.default)
1116        })
1117
1118        $button.click(function(){
1119            if(fullScreenActived){
1120                $parentContainer.css({
1121                    position:"relative",
1122                    width:defaultSizeParentContainer.width,
1123                    height:defaultSizeParentContainer.height
1124                })
1125                $("html, body").css({
1126                    overflow:"visible"
1127                });
1128                $button.html(`<i class="fa fa-${optBtnFullScreen.icon.maximize}" aria-hidden="true"></i>`)
1129                $("html, body").scrollTop($parentContainer.offset().top - 100)
1130            }else{
1131                var mainNavHeight = document.getElementById("mainNav").offsetHeight
1132                $parentContainer.css({
1133                    position:"absolute",
1134                    height:`calc(100vh - ${mainNavHeight}px)`,
1135                    width:"100vw",
1136                    top:0,
1137                    left:0,
1138                    zIndex:999
1139                })
1140                $("html, body").scrollTop(0).css({
1141                    overflow:"hidden"
1142                });
1143                $button.html(`<i class="fa fa-${optBtnFullScreen.icon.minimize}" aria-hidden="true"></i>`)
1144            }
1145
1146            setTimeout(function(){
1147                _map.invalidateSize()
1148            }, 400)
1149
1150            fullScreenActived = !fullScreenActived;
1151        })
1152    }
1153
1154    var initBtnActions = function(){
1155        var btnActionItems = "";
1156        for(let i in _options.actionButtons){
1157            _options.actionButtons[i].id = `btn-map-action_${_utils.generateRandomId()}`;
1158            btnActionItems += `<li id="${_options.actionButtons[i].id}"><i class="fa fa-${_options.actionButtons[i].icon}" aria-hidden="true"></i></li>`
1159        }
1160
1161        var btnActions = `
1162            <div class="btn-map-actions-container">
1163                <ul class="btn-map-actions">
1164                    ${btnActionItems}
1165                </ul>
1166                <button id="btn-toggle-map-actions"><i class="fa fa-plus" aria-hidden="true"></i></button>
1167            </div>
1168        `
1169        $(_options.parentContainer).append(btnActions);
1170
1171        $('#btn-toggle-map-actions').click(function(){
1172            $(this).toggleClass('active')
1173            $(".btn-map-actions").toggleClass('active')
1174        })
1175        for(let i in _options.actionButtons){
1176            $("#"+ _options.actionButtons[i].id).click(function(){
1177                if(_options.actionButtons[i].onclick){
1178                    _options.actionButtons[i].onclick()
1179                }
1180                $('#btn-toggle-map-actions').removeClass("active")
1181                $(".btn-map-actions").removeClass('active')
1182            })
1183        }
1184    }
1185
1186    var init = function(){
1187        if($(options.container).length > 0){
1188            initVar()
1189            initView()
1190            _context.showLoader();
1191            _map = L.map(_options.container, _options.mapOpt);
1192            _context.setTile(_options.mapCustom.tile)
1193            L.control.zoom({
1194                position: 'topleft',
1195            }).addTo(_map);
1196
1197            if(_options.elts){
1198                _context.addElts(_options.elts)
1199            }else{
1200                setTimeout(function(){
1201                    _map.invalidateSize()
1202                }, 400)
1203            }
1204        }
1205    }
1206    /* <========== private methodes */
1207
1208    /* getters & setters ==========> */
1209    this.getMap = function(){
1210        return _map;
1211    }
1212    this.getOptions = function(){
1213        return _options;
1214    }
1215    this.setOptions = function(newOptions){
1216        _options = newOptions;
1217    }
1218    this.getUtils = function(){
1219        return _utils;
1220    }
1221    this.getFiltres = function(){
1222        return _filtres;
1223    }
1224    /* <========== getters & setters */
1225
1226    /* public methodes ==========> */
1227    this.addElts = function (data, refresh) {
1228        _context.clearMap()
1229        if(_options.mapCustom.addElts){
1230            _options.mapCustom.addElts(_context, data)
1231        }else{
1232            _options.legende.push("Autre");
1233            _options.legende = unique(_options.legende);
1234            if(_options.markerType == "geoshape"){
1235                var itteration = 1;
1236                data = data.features ? data.features : data;
1237                $.each(data, function(k, v){
1238                    var geoV = {};
1239                    if(v.type && v.geometry){
1240                        geoV = v;
1241                    }else if(v.geoShape){
1242                        geoV = {
1243                            type: "Feature",
1244                            properties: {
1245                                name: v.name
1246                            },
1247                            geometry: v.geoShape
1248                        }
1249                    }
1250                    _context.addGeoJSON(geoV);
1251                    if(itteration == Object.keys(data).length){
1252                        var geoJson = L.geoJSON(_options.arrayBounds, {
1253                            onEachFeature: function(feature, layer){
1254                                layer.on("mouseover", function(){
1255                                    geoJson.setStyle({color: 'gray', op
1255acity: 0.5});
1256                                    this.setStyle({
1257                                        color: '#0000FF',
1258                                        opacity: 1
1259                                    })
1260                                })
1261                                layer.on("mouseout", function(){
1262                                    geoJson.setStyle({color: '#0000FF', opacity: 0.5});
1263                                })
1264                                _context.addPopUp(layer, feature.properties);
1265                            },
1266                            style: {
1267                                color: "#0000FF"
1268                            }
1269                        });
1270                        _options.geoJSONlayer.addLayer(geoJson);
1271                        _map.addLayer(geoJson);
1272                        _context.hideLoader();
1273                        _map.invalidateSize();
1274                        _map.fitBounds(geoJson.getBounds())
1275                    }
1276                    itteration++;
1277                });
1278            }else if(_options.markerType == "heatmap"){
1279                if(typeof refresh == 'undefined' || refresh){
1280                    _options.clickedLegende = "";
1281                    _options.data = data;
1282                }
1283                var cfg = {
1284                    // radius should be small ONLY if scaleRadius is true (or small radius is intended)
1285                    // if scaleRadius is false it will be the constant radius used in pixels
1286                    "radius": 20,
1287                    "maxOpacity": .75,
1288                    // scales the radius based on map zoom
1289                    "scaleRadius": false,
1290                    // if set to false the heatmap uses the global maximum for colorization
1291                    // if activated: uses the data maximum within the current map boundaries
1292                    //   (there will always be a red spot with useLocalExtremas true)
1293                    "useLocalExtrema": true,
1294                    // which field name in your data represents the latitude - default "lat"
1295                    latField: 'lat',
1296                    // which field name in your data represents the longitude - default "lng"
1297                    lngField: 'lng',
1298                    // which field name in your data represents the data value - default "value"
1299                    valueField: 'count',
1300                    gradient: {
1301                        0.25: "rgb(0,0,255)",
1302                        0.55: "rgb(3 173 3)",
1303                        0.85: "#f9f952",
1304                        1.0: '#ff2b40'
1305                    }
1306                };
1307                var heatMapData = {max: 2000, data: []};
1308                var heatmapLayer = new HeatmapOverlay(cfg);
1309                var geoJson = [];
1310                var i = 1;
1311                var geoJsonLayer = null;
1312                $.each(data, function(k, v){
1313                    if(typeof v.geoShape != "undefined"){
1314                        _context.addGeoJSON({
1315                            type: "Feature",
1316                            properties: {
1317                                name: v.name
1318                            },
1319                            geometry: v.geoShape
1320                        });
1321                    }
1322                    if(_filtres.result(v) && typeof v.geoShape == "undefined" && typeof v.geo != "undefined"){
1323                        heatMapData.data.push({lat: v.geo.latitude, lng: v.geo.longitude, count: 1});
1324                        var latLon = [v.geo.latitude, v.geo.longitude];
1325                        _options.arrayBounds.push(latLon)
1326                    }
1327                    if(i == Object.keys(data).length){
1328                        geoJsonLayer = L.geoJSON(_options.arrayBounds, {
1329                            onEachFeature: function(feature, layer){
1330                                layer.on("mouseover", function(){
1331                                    geoJsonLayer.setStyle({color: 'gray', opacity: 0.5});
1332                                    this.setStyle({
1333                                        color: '#0000FF',
1334                                        opacity: 1
1335                                    })
1336                                })
1337                                layer.on("mouseout", function(){
1338                                    geoJsonLayer.setStyle({color: '#0000FF', opacity: 0.5});
1339                                })
1340                                _context.addPopUp(layer, feature.properties);
1341                            },
1342                            style: {
1343                                color: "#0000FF"
1344                            }
1345                        });
1346                        heatmapLayer.setData(heatMapData);
1347                        _options.geoJSONlayer.addLayer(geoJsonLayer);
1348                        _options.geoJSONlayer.addLayer(heatmapLayer);
1349                        _map.addLayer(geoJsonLayer);
1350                        _map.addLayer(heatmapLayer);
1351                    }
1352                    i++;
1353                })
1354                _context.hideLoader();
1355                _map.invalidateSize();
1356                _context.fitBounds(data);
1357            }else{
1358                if(typeof refresh == 'undefined' || refresh){
1359                    _options.clickedLegende = "";
1360                    _options.data = data;
1361                }
1362                var geoJson = [];
1363                $.each(data, function(k, v){
1364                    if(!_options.data[k])
1365                        _options.data[k] = v;
1366                    if(_options.dynamicLegende == true || _options.dynamicLegende == 'true'){
1367                        if(v.data){
1368                            _options.legende = unique(_options.legende.concat(Object.keys(v.data)))
1369                        }else if(v[_options.groupBy] instanceof Array || v[_options.groupBy] instanceof Object){
1370                            _options.legende = unique(_options.legende.concat(Object.keys(v[_options.groupBy])))
1371                        }else{
1372                            _options.legende.push((!v[_options.groupBy] || v[_options.groupBy] == "" ? "Autre" : v[_options.groupBy]));
1373                            _options.legende = unique(_options.legende);
1374                        }
1375                    }
1376                    if(typeof v.geoShape != "undefined"){
1377                        geoJson.push({
1378                                type: "Feature",
1379                                properties: {
1380                                    name: v.name,
1381                                    id: k
1382                                },
1383                                geometry: v.geoShape
1384                        });
1385                    }
1386                    if(_filtres.result(v) && typeof v.geoShape == "undefined" && typeof v.geo != "undefined"){
1387                        if(typeof refresh == "undefined" || refresh){
1388                            _options.legendeActivated = unique(_options.legendeActivated.concat(_options.legende));
1389                        }
1390                        v.id = k;
1391                        _context.addMarker({ elt: v, legende: _options.legende, legendeActivated: _options.legendeActivated })
1392                        _options.mapCustom.lists.createItemList(k, v, _options.container)
1393                    }
1394                })
1395                _options.geoJSONlayer.addLayer(L.geoJSON(geoJson, {
1396                    onEachFeature: function(feature, layer){
1397                        _context.addPopUp(layer, feature);
1398                        if(_options.mapOpt.onclickMarker){
1399                            layer.on('click', function(){
1400                                _options.mapOpt.onclickMarker(feature);
1401                            });
1402                        }
1403                    },
1404                    style: {
1405                        color: "#0000FF"
1406                    }
1407                }));
1408                _map.addLayer(_options.geoJSONlayer);
1409                if(_options.activeCluster)
1410                    _map.addLayer(_options.markersCluster)
1411                _utils.bindMap();
1412        
1413                if(_options.forced){
1414                    if(_options.forced.latLon && _options.forced.zoom){
1415                        _map.setView(_options.forced.latLon, _options.forced.zoom)
1416                    }else{
1417                        if(_options.forced.latLon)
1418                            _map.panTo(_options.forced.latLon)
1419                        if(_options.forced.zoom)
1420                            _map.setZoom(_options.forced.zoom)
1421                    }
1422                }
1423                if(_options.showLegende){
1424                    // if(document.querySelectorAll("#info-pane").length < 1){
1425                    //     $("[id="+_options.container+"]").append('<div id="info-pane" class="leaflet-bar chart-container" style="position: absolute;right: 20px;top: 0;z-index: 9999999;background: white;display: none; ">'+
1426                    //         '<div style="max-height: 500px;overflow-y: auto;"><svg id="chartCanvas"></svg></div>'+
1427                    //         '<button id="chartBtn" class="btn btn-sm">Enregistrer</button>'+
1428                    //     '</div>');
1429                    // }
1430                    if(document.querySelectorAll(".chart-legende-container").length < 1){
1431                        legende = L.control({
1432                            position: "topright"
1433                        });
1434                        legende.onAdd = function(map){
1435                            var div = L.DomUtil.create('div', 'chart-legende-container');
1436                            var str = '<div style="max-height: 500px;overflow-y: auto;"><svg id="chartCanvas"></svg></div>';
1437    
1438                            str += '<button id="chartBtn" class="btn btn-sm">Enregistrer</button>';
1439                            div.innerHTML = str;
1440                            return div;
1441                        }
1442                        legende.addTo(_map)
1443                    }
1444                    var Svg = d3.select("#chartCanvas")
1445                        .attr("width", 200)
1446                        .attr("height", _options.legende.length * 20 + 20);
1447                    Svg.selectAll(".d3map-legend").remove();
1448                    _options.lastLegende = JSON.parse(JSON.stringify(_options.legendeActivated));
1449                    // Add one dot in the legend for each name.
1450                    var group = Svg.selectAll("mydots")
1451                    .data(_options.legende)
1452                    .join((enter) => {
1453                        let g = enter;
1454                        g = g.append("g").attr("height", "20px").attr("width", "100%").attr('class', 'd3map-legend').style("cursor", "pointer");
1455                        g.append("circle")
1456                            .attr("cx", 20)
1457                            .attr("cy", function(d,i){ return 20 + i*20}) // 20 is where the first dot appears. 25 is the distance between dots
1458                            .attr("r", 7)
1459                            .style("fill", function(d){ return (_options.legendeActivated.indexOf(d) > -1 ? _options.colors(d) : "transparent")})
1460                            .style("stroke", function(d){ return _options.colors(d)})
1461                            .on('click', function(d, i){
1462                                var circle = d3.select(this);
1463                                if(circle.attr("class") === "disabled"){
1464                                    circle.attr("class", "");
1465                                    circle.style("fill", function(d){ return _options.colors(d)});
1466                                    _options.lastLegende.push(i);
1467                                }else{
1468                                    circle.attr("class", "disabled");
1469                                    circle.style("fill", "transparent");
1470                                    if(_options.lastLegende.indexOf(i) > -1){
1471                                        _options.lastLegende.splice(_options.lastLegende.indexOf(i), 1)
1472                                    }
1473                                }
1474                                if(JSON.stringify(_options.legendeActivated.sort()) == JSON.stringify(_options.lastLegende.sort())){
1475                                    $("#chartBtn").hide();
1476                                }else{
1477                                    $("#chartBtn").show();
1478                                }
1479                            })
1480                        g.append("text")
1481                            .attr("x", 35)
1482                            .attr("y", function(d,i){ return 20 + i*20}) // 20 is where the first dot appears. 25 is the distance between dots
1483                            .style("fill", function(d){ return _options.colors(d)})
1484                            .text(function(d){ return d})
1485                            .attr("text-anchor", "left")
1486                            .style("alignment-baseline", "middle")
1487                            .on('click', function(d, i){
1488                                if(_options.clickedLegende == i){
1489                                    _context.clearMap()
1490                                    _context.addElts(_options.data);
1491                                }else{
1492                                    _options.clickedLegende = i;
1493                                    i = (i == 'Autre' ? '' : i);
1494                                    var data = Object.fromEntries(Object.entries(_options.data).
1495                                        filter(([key, val]) => {
1496                                            if(_options.dynamicLegende == false || _options.dynamicLegende == 'false'){
1497                                                return i == '' ? (!val[_options.legendeVar] || (val[_options.legendeVar] && val[_options.legendeVar].some(ai => _options.legende.includes(ai)) == false)) : (!val[_options.legendeVar] ? false : val[_options.legendeVar].includes(i));
1498                                            }else{
1499                                                return (i == '' ? (!val[_options.groupBy] || val[_options.groupBy] == '') : val[_options.groupBy] == i )
1500                                            }
1501                                        }))
1502                                    _context.addElts(data, false);
1503                                    }
1504                            })
1505                        return g;
1506                    })
1507                    
1508                    
1509                    d3.select("#chartBtn").on('click', function(){
1510                        $(".mapLoading").show();
1511                        _options.legendeActivated = JSON.parse(JSON.stringify(_options.lastLegende))
1512                        setTimeout(function(){
1513                            _context.addElts(data, false);
1514                        }, 100)
1515                    })
1516                    $("#chartBtn").hide();
1517                    $("#info-pane").show();
1518                }else{
1519                    $("#info-pane").hide();
1520                }
1521                _context.hideLoader();
1522                _map.invalidateSize();
1523                _context.fitBounds(data);
1524            }
1525        }
1526    }
1527
1528    this.addGeoJSON = function(params){
1529        _options.arrayBounds.push(params);
1530
1531    }
1532
1533    this.addMarker = function(params){
1534        if(_options.mapCustom.addMarker){
1535            _options.mapCustom.addMarker(_context, params)
1536        }else{
1537            if( params.elt && params.elt.geo && params.elt.geo.latitude && params.elt.geo.longitude){
1538                if(!params.opt)
1539                    params.opt = {}
1540                params.opt.icon = _options.mapCustom.icon.getIcon(params)
1541    
1542                var latLon = [params.elt.geo.latitude, params.elt.geo.longitude];
1543                this.setDistanceToMax(latLon);
1544    
1545                var marker = L.marker(latLon, params.opt);
1546                _options.markerList[params.elt.id] = marker;
1547    
1548                if(_options.activePopUp){
1549                    _context.addPopUp(marker, params.elt)
1550                }
1551    
1552                _options.arrayBounds.push(latLon)
1553    
1554                if(_options.activeCluster){
1555                    _options.markersCluster.addLayer(marker)
1556                }else{
1557                    marker.addTo(_map);
1558                    if(params.center){
1559                        _map.panTo(latLon)
1560                    }
1561                }
1562    
1563                if(_options.mapOpt.doubleClick){
1564                    marker.on('click', function(e){
1565                        this.openPopup()
1566                        coInterface.bindLBHLinks();
1567                    })
1568                    marker.on('dbclick', function(e){
1569                        _context.centerOne(params.elt.geo.latitude, params.elt.geo.longitude)
1570                        coInterface.bindLBHLinks();
1571                    })
1572                }else{
1573                    marker.on('click', function (e) {
1574                        if(_options.mapOpt.onclickMarker){
1575                            _options.mapOpt.onclickMarker(params)
1576                        }else{
1577                            _context.centerOne(params.elt.geo.latitude, params.elt.geo.longitude)
1578                            coInterface.bindLBHLinks();
1579                        }
1580                    });
1581                }
1582    
1583                if(_options.mapOpt.mouseOver){
1584                    marker.on('mouseover', function (e) {
1585                        this.openPopup()
1586                        coInterface.bindLBHLinks();
1587                    });
1588                    marker.on('mouseout', function (e) {
1589                        var thismarker = this;
1590                        setTimeout(function(){
1591                            thismarker.closePopup();
1592                        }, 2000);
1593                    });
1594                }
1595            }
1596        }
1597    }
1598
1599    this.setDistanceToMax = function(latLon){
1600        if(Object.keys(_options.markerList).length > 0){
1601            var latLonObj = L.latLng(latLon[0], latLon[1]);
1602            $.each(_options.markerList, function(k, v){
1603                var dist = latLonObj.distanceTo(v.getLatLng())
1604                _options.distanceToMax = (dist > _options.distanceToMax) ? dist:_options.distanceToMax
1605            })
1606        }
1607    }
1608
1609    this.setZoomByDistance = function(){
1610        var markerListCount = Object.keys(_options.markerList).length
1611        if(markerListCount == 0){
1612            _options.mapOpt.zoom = 0;
1613            _options.mapOpt.offset.x = 0;
1614            _options.mapOpt.offset.y = 0;
1615        }else if(markerListCount == 1){
1616            _options.mapOpt.zoom = 14;
1617			_options.mapOpt.offset.x = 0.002;
1618			_options.mapOpt.offset.y = 0.003;
1619        }else{
1620            if(_options.distanceToMax > 9000000){
1621                _options.mapOpt.zoom = 2;
1622				_options.mapOpt.offset.x = 0;
1623				_options.mapOpt.offset.y = 15;
1624            }else if(_options.distanceToMax > 7500000){
1625                _options.mapOpt.zoom = 3;
1626				_options.mapOpt.offset.x = 0;
1627				_options.mapOpt.offset.y = 15;
1628            }else if(_options.distanceToMax > 6000000){
1629                _options.mapOpt.zoom = 4;
1630				_options.mapOpt.offset.x = 0;
1631				_options.mapOpt.offset.y = 0.2;
1632            }else if(_options.distanceToMax > 50000){
1633                _options.mapOpt.zoom = 6;
1634				_options.mapOpt.offset.x = 0;
1635				_options.mapOpt.offset.y = 0.2;
1636            }else if(_options.distanceToMax > 20000){
1637                _options.mapOpt.zoom = 8;
1638				_options.mapOpt.offset.x = 0;
1639				_options.mapOpt.offset.y = 0.2;
1640            }else if(_options.distanceToMax > 10000){
1641                _options.mapOpt.zoom = 10;
1642				_options.mapOpt.offset.x = 0;
1643				_options.mapOpt.offset.y = 0.2;
1644            }else{
1645                _options.mapOpt.zoom = 12;
1646				_options.mapOpt.offset.x = 0;
1647				_options.mapOpt.offset.y = 0.2;
1648            }
1649        }
1650
1651        if(!_options.mapOpt.menuRight){
1652            _options.mapOpt.offset.x = 0;
1653			_options.mapOpt.offset.y = 0;
1654        }
1655    }
1656
1657    this.clearMap = function(){
1658        if(_options.markersCluster){
1659            _options.markersCluster.clearLayers()
1660        }
1661        if(_options.geoJSONlayer){
1662            _options.geoJSONlayer.eachLayer(function(layer){
1663                _map.removeLayer(layer)
1664            });
1665        }
1666        if(Object.keys(_options.markerList).length > 0){
1667            $.each(_options.markerList, function(){
1668                _map.removeLayer(this)
1669            })
1670        }
1671        if(_options.markerSingleList && Object.keys(_options.markerSingleList)){
1672            $.each(_options.markerSingleList, function(){
1673                _map.removeLayer(this)
1674            })
1675        }
1676        _options.markerList = {};
1677        _options.arrayBounds = [];
1678        _options.bounds = null;
1679
1680        _context.showLoader()
1681        $(_options.parentContainer + " .liste_map_element").html("")
1682    }
1683
1684    this.addPopUp = function(marker, elt=null, open=false){
1685        var popupSimple = _options.mapCustom.getPopup(elt)
1686        marker.bindPopup(popupSimple, {width:"300px"}).openPopup();
1687        if(open)
1688            marker.openPopup();
1689    }
1690
1691    this.centerOne = function(lat, lon){
1692        if(_filtres.timeoutAddCity)
1693            clearTimeout(_filtres.timeoutAddCity)
1694        _filtres.timeoutAddCity = setTimeout(function(){
1695            var moveLat = "0.0008";
1696            var moveLon = "0.000";
1697            
1698            lat = parseFloat(lat) + parseFloat(moveLat);
1699            lon = parseFloat(lon) + parseFloat(moveLon);
1700            
1701            var center = [lat, lon];
1702
1703            _map.setView(center, _options.mapOpt.centerOneZoom)
1704        }, 500)
1705    }
1706
1707    this.showLoader = function(){
1708        $(".mapLoading").show();
1709        var heightMap = $(_options.parentContainer).outerHeight();
1710        var widthMap = $(_options.parentContainer).outerWidth();
1711        if ($(_options.parentContainer + " #menuRight" + _options.parentContainerName).is(":visible")) {
1712			widthMap = widthMap - $(_options.parentContainer + " #menuRight" + _options.parentContainerName).outerWidth();
1713        }
1714        coInterface.showLoader(".mapLoading", trad.currentlyresearching);
1715        var marginLeftLoader = (widthMap - $(".mapLoading").outerWidth()) / 2;
1716        var marginTopLoader = (heightMap - $(".mapLoading").outerHeight()) / 2;
1717        $(".mapLoading").css({ "margin-left": marginLeftLoader + "px", "margin-top": marginTopLoader + "px" });
1718    }
1719    this.hideLoader = function(){
1720        $(".mapLoading").fadeOut();
1721    }
1722
1723    this.setTile = function(tile){
1724        if (tile === "mapbox" && $('script[src="https://api.mapbox.com/mapbox.js/v3.2.1/mapbox.js"]').length){
1725            var accessToken = 'pk.eyJ1IjoiY29tbXVuZWN0ZXI5NzQiLCJhIjoiY2tkOHF3cTZ1MDZmNzJzbWk4M3pnZ2dvbCJ9.J9aOUObe8B9lH-8QLmDzNw';
1726
1727            var mapboxTiles = L.tileLayer('https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}
1727?access_token=' + accessToken, {
1728				attribution: '© <a href="https://www.mapbox.com/feedback/">Mapbox</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
1729            });
1730            _map.addLayer(mapboxTiles)
1731        } else if (tile === "maptiler" && $('script[src="https://api.mapbox.com/mapbox.js/v3.2.1/mapbox.js"]').length) {
1732            var accessToken = '2FAeSQOpVCo7WdsCmujj';
1733
1734            var mapboxTiles = L.tileLayer('https://api.maptiler.com/maps/eb5fb1d0-58c0-47ab-af0a-86ed434e011c/256/{z}/{x}/{y}@2x.png?key=' + accessToken, {
1735                "attribution": "<a href=\"https://www.maptiler.com/copyright/\" target=\"_blank\">&copy; MapTiler</a> <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">&copy; OpenStreetMap contributors</a>"
1736            });
1737            _map.addLayer(mapboxTiles)
1738        }else if(tile === "toner-lite"){
1739            // L.tileLayer('https://tiles.stadiamaps.com/tiles/stamen_toner_lite/{z}/{x}/{y}{r}.png', {
1740			// 	attribution: '&copy; <a href="https://stadiamaps.com/" target="_blank">Stadia Maps</a> <a href="https://stamen.com/" target="_blank">&copy; Stamen Design</a> &copy; <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> &copy; <a href="https://www.openstreetmap.org/about" target="_blank">OpenStreetMap</a> contributors'
1741			// }).addTo(_map)
1742
1743            var accessToken = '2FAeSQOpVCo7WdsCmujj';
1744
1745			var mapboxTiles = L.tileLayer('https://api.maptiler.com/maps/toner-v2-lite/256/{z}/{x}/{y}@2x.png?key=' + accessToken, {
1746				"attribution": "<a href=\"https://www.maptiler.com/copyright/\" target=\"_blank\">&copy; MapTiler</a> <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">&copy; OpenStreetMap contributors</a>", 
1747			});
1748			_map.addLayer(mapboxTiles)
1749        }else if(tile === "satelite"){
1750            // L.tileLayer('https://tiles.stadiamaps.com/tiles/stamen_toner_lite/{z}/{x}/{y}{r}.png', {
1751			// 	attribution: '&copy; <a href="https://stadiamaps.com/" target="_blank">Stadia Maps</a> <a href="https://stamen.com/" target="_blank">&copy; Stamen Design</a> &copy; <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a> &copy; <a href="https://www.openstreetmap.org/about" target="_blank">OpenStreetMap</a> contributors'
1752			// }).addTo(_map)
1753
1754            var accessToken = '2FAeSQOpVCo7WdsCmujj';
1755
1756			var mapboxTiles = L.tileLayer('https://api.maptiler.com/maps/toner-v2-lite/256/{z}/{x}/{y}@2x.png?key=' + accessToken, {
1757				"attribution": "<a href=\"https://www.maptiler.com/copyright/\" target=\"_blank\">&copy; MapTiler</a> <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">&copy; OpenStreetMap contributors</a>", 
1758			});
1759			_map.addLayer(mapboxTiles)
1760        }else if(tile == "mapnik"){
1761            L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
1762                maxZoom: 19,
1763                attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
1764            }).addTo(_map);
1765        }else{
1766            if(baseUrl.indexOf("www.communecter.org") > -1){
1767                if($('script[src="https://api.mapbox.com/mapbox.js/v3.2.1/mapbox.js"]').length){
1768                    var accessToken = '2FAeSQOpVCo7WdsCmujj';
1769                    var mapboxTiles = L.tileLayer('https://api.maptiler.com/maps/eb5fb1d0-58c0-47ab-af0a-86ed434e011c/256/{z}/{x}/{y}@2x.png?key=' + accessToken, {
1770                        "attribution": "<a href=\"https://www.maptiler.com/copyright/\" target=\"_blank\">&copy; MapTiler</a> <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">&copy; OpenStreetMap contributors</a>"
1771                    });
1772                    _map.addLayer(mapboxTiles)
1773                }else{
1774					var accessToken = '2FAeSQOpVCo7WdsCmujj';
1775
1776					var mapboxTiles = L.tileLayer('https://api.maptiler.com/maps/toner-v2/256/{z}/{x}/{y}@2x.png?key=' + accessToken, {
1777						"attribution": "<a href=\"https://www.maptiler.com/copyright/\" target=\"_blank\">&copy; MapTiler</a> <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">&copy; OpenStreetMap contributors</a>",
1778					});
1779
1780					this.map.addLayer(mapboxTiles)
1781                }
1782            }else{
1783                L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
1784                    attribution: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>'
1785                }).addTo(_map);
1786            }
1787        }
1788    }
1789
1790    this.addPolygon = function(data){
1791        var polygon = L.polygon(data).addTo(_map)
1792        _context.addPopUp(polygon)
1793    }
1794
1795    this.fitBounds = function(){
1796        if(_options.arrayBounds.length > 0){
1797            _options.arrayBounds.forEach(function(element){
1798
1799            });
1800            _map.fitBounds(_options.arrayBounds)
1801            if(_map.getZoom() == 0){
1802                _map.setZoom(3)
1803            }
1804        }else{
1805            _map.panTo(_options.mapOpt.center);
1806            _map.setZoom(_options.mapOpt.zoom);
1807            _map.invalidateSize();
1808        }
1809    }
1810    /* public methodes ==========> */
1811
1812    /* call init when constructing the object */
1813    init()
1814}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.