PageSourceSearch

https://nichbelize.org/wp-content/plugins/interactive-geo-maps/assets/public/map-service/app.js?ver=1.6.30

js nichbelize.org collected 2026-10-02 18:18:34 UTC 191,303 bytes, 5,794 lines download raw bytes

1'use strict';
2
3function _typeof(obj) {
4  '@babel/helpers - typeof';
5  if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') {
6    _typeof = function _typeof(obj) {
7      return typeof obj;
8    };
9  } else {
10    _typeof = function _typeof(obj) {
11      return obj &&
12        typeof Symbol === 'function' &&
13        obj.constructor === Symbol &&
14        obj !== Symbol.prototype
15        ? 'symbol'
16        : typeof obj;
17    };
18  }
19  return _typeof(obj);
20}
21
22/**
23 * Geocluster function. Original:
24 * https://github.com/yetzt/node-geocluster
25 * and adapted to work with iMaps
26 */
27function geocluster(elements, bias, defaults, tooltipTemplate) {
28  bias = parseFloat(bias);
29  if (!(this instanceof geocluster)) {
30    return new geocluster(elements, bias, defaults, tooltipTemplate);
31  }
32  return this._cluster(elements, bias, defaults, tooltipTemplate);
33} // degrees to radians
34
35geocluster.prototype._toRad = function (number) {
36  return (number * Math.PI) / 180;
37}; // geodetic distance approximation
38
39geocluster.prototype._dist = function (lat1, lon1, lat2, lon2) {
40  var dlat = this._toRad(lat2 - lat1);
41
42  var dlon = this._toRad(lon2 - lon1);
43
44  var a =
45    Math.sin(dlat / 2) * Math.sin(dlat / 2) +
46    Math.sin(dlon / 2) *
47      Math.sin(dlon / 2) *
48      Math.cos(this._toRad(lat1)) *
49      Math.cos(this._toRad(lat2));
50  return Math.round(2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) * 6371 * 100) / 100;
51};
52
53geocluster.prototype._centroid = function (set) {
54  var cetroidObj = Object.values(set).reduce(
55    function (accumulator, currentValue) {
56      return {
57        latitude: accumulator.latitude + currentValue.latitude,
58        longitude: accumulator.longitude + currentValue.longitude,
59      };
60    },
61    {
62      latitude: 0,
63      longitude: 0,
64    }
65  );
66  cetroidObj = Object.values(cetroidObj).map(function (e) {
67    return e / Object.keys(set).length;
68  });
69  return cetroidObj;
70};
71
72geocluster.prototype._clean = function (data) {
73  return data.map(function (cluster) {
74    return [cluster.centroid, cluster.elements];
75  });
76};
77
78geocluster.prototype._cluster = function (elements, bias, defaults, tooltipTemplate) {
79  var self = this,
80    clusterMapCollection = [];
81  var tempMarker; // set bias to 1 on default
82
83  if (typeof bias !== 'number' || isNaN(bias)) {
84    bias = 1;
85  }
86
87  // derive threshold from bias
88  var threshold = 10000 * bias;
89  var clusterMap = [];
90
91  // generate random initial cluster map
92  clusterMap.push({
93    centroid: elements[Math.floor(0.5 * elements.length)],
94    elements: [],
95    fill: defaults.fill,
96    hover: defaults.hover,
97    radius: defaults.radius,
98  });
99
100  // loop elements and distribute them to clusters
101  var changing = true;
102
103  while (changing === true) {
104    var newCluster = false;
105    var clusterChanged = false; // iterate over elements
106
107    elements.forEach(function (e) {
108      var closestDist = Infinity;
109      var closestCluster = null; // find closest cluster
110
111      clusterMap.forEach(function (cluster, ci) {
112        // distance to cluster
113        var dist = self._dist(
114          e.latitude,
115          e.longitude,
116          clusterMap[ci].centroid.latitude,
117          clusterMap[ci].centroid.longitude
118        );
119
120        if (dist < closestDist) {
121          closestDist = dist;
122          closestCluster = ci;
123        }
124      }); // is the closest distance smaller than the stddev of elements?
125
126      if (closestDist < threshold || closestDist === 0) {
127        // put element into existing cluster
128        clusterMap[closestCluster].elements.push(e);
129      } else {
130        // create a new cluster with this element
131        clusterMap.push({
132          centroid: e,
133          elements: [e],
134        });
135        newCluster = true;
136      }
137    }); // delete empty clusters from clusterMap
138
139    clusterMap = clusterMap.filter(function (cluster) {
140      return cluster.elements.length > 0;
141    }); // calculate the clusters centroids and check for change
142
143    clusterMap.forEach(function (cluster, ci) {
144      var centroid = self._centroid(cluster.elements);
145
146      changing = false;
147
148      if (
149        centroid.latitude !== cluster.centroid.latitude ||
150        centroid.longitude !== cluster.centroid.longitude
151      ) {
152        clusterMap[ci].centroid = centroid;
153        clusterChanged = true;
154      }
155    });
155 // loop cycle if clusters have changed
156
157    if (!clusterChanged && !newCluster) {
158      changing = false;
159    } else {
160      // remove all elements from clusters and run again
161      if (changing) {
162        clusterMap = clusterMap.map(function (cluster) {
163          cluster.elements = [];
164          return cluster;
165        });
166      }
167    }
168  }
169
170  clusterMap = clusterMap.map(function (cluster) {
171    if (cluster.elements.length === 1) {
172      clusterMapCollection.push(cluster.elements[0]);
173    } else {
174      tempMarker = {
175        id: '',
176        label: cluster.elements.length,
177        name: '',
178        value: cluster.elements.length,
179        cluster: true,
180        latitude: cluster.centroid[0],
181        longitude: cluster.centroid[1],
182        elements: cluster.elements,
183        content: '',
184        fill: defaults.fill,
185        hover: defaults.hover,
186        radius: defaults.radius,
187        action: '',
188      }; // in case we have a custom tooltipTemplate
189
190      if (tooltipTemplate) {
191        tempMarker.tooltipTemplate = tooltipTemplate;
192      }
193
194      clusterMapCollection.push(tempMarker);
195    }
196  }); // compress result
197
198  return clusterMapCollection;
199};
200/* ROUTER */
201var iMapsRouter = {};
202
203iMapsRouter.getGeoFiles = function (regionClicked) {
204  var regionID = regionClicked.id.toString(),
205    regionName = regionClicked.name,
206    urlappend,
207    varappend,
208    geoFiles = {},
209    continents = [
210      'southAmerica',
211      'northAmerica',
212      'europe',
213      'middleEast',
214      'asia',
215      'oceania',
216      'africa',
217      'antarctica',
218    ]; // continents
219
220  if (continents.includes(regionID)) {
221    urlappend = 'region/world/';
222    varappend = '_region_world_';
223  } // us maps
224  else if (regionID.includes('US-')) {
225    urlappend = 'region/usa/';
226    varappend = '_region_usa_';
227    regionID = regionID.substr(-2).toLowerCase();
228  } else {
229    urlappend = '';
230    varappend = '_';
231    regionID = regionName.toLowerCase().replace('united states', 'usa');
232  }
233
234  geoFiles.src = 'https://cdn.amcharts.com/lib/4/geodata/' + urlappend + regionID + 'Low.js';
235  geoFiles.map = 'am4geodata' + varappend + regionID + 'Low';
236  geoFiles.title = regionName;
237  return geoFiles;
238};
239
240iMapsRouter.getAllSrc = function () {
241  var sources = {},
242    paths = {
243      main: '../../vendor/geodata',
244      usa: '../../vendor/geodata/region/usa',
245      world: '../../vendor/geodata/region/world',
246    };
247
248  var fs = require('fs');
249
250  Object.keys(paths).forEach(function (item) {
251    sources[item] = [];
252    fs.readdir(paths[item], function (err, files) {
253      files.forEach(function (file) {
254        sources[item].push(file);
255      });
256    });
257  });
258  return sources;
259};
260/**
261 * get map am4charts variable name based on name value
262 */
263
264iMapsRouter.getVarByName = function (variable) {
265  if (typeof variable === 'undefined') {
266    return 'am4geodata_worldLow';
267  } else if (variable.includes('custom')) {
268    return variable;
269  }
270
271  variable = variable.replace(/\//g, '_');
272  return 'am4geodata_' + variable;
273};
274
275iMapsRouter.getCleanMapName = function (mapName, id) {
276  if (typeof mapName === 'undefined') {
277    return false;
278  }
279
280  mapName = mapName.replace('Low', '');
281  mapName = mapName.replace('High', '');
282
283  if (mapName === 'custom') {
284    mapName += id;
285  }
286
287  return mapName;
288};
289
290iMapsRouter.iso2cleanName = function (iso, mapID) {
291  var countries = iMapsRouter.getCountries();
292  var continents = [
293    'africa',
294    'antarctiva',
295    'asia',
296    'europe',
297    'middleEast',
298    'northAmerica',
299    'oceania',
300    'southAmerica',
301    'centralAmerica',
302  ];
303  var tempIso;
304  var series = iMapsManager.maps[mapID].seriesIndex;
305  var match = false;
306  console.log('ISO: ' + iso); // exceptions
307
308  if ('VA' === iso) {
309    return 'vatican';
310  } else if ('US' === iso) {
311    return 'usa';
312  } else if (iso.includes('CA-')) {
313    return 'region/canada/' + iso.replace('CA-', '').toLowerCase();
314  } else if (iso.includes('MX-')) {
315    return 'region/mexico/' + iso.replace('MX-', '').toLowerCase();
316  } else if (continents.includes(iso)) {
317    return 'region/world/' + iso;
318  } else if (iso.includes('US-')) {
319    tempIso = [
320      'region/usa/' + iso.replace('US-', '').toLowerCase(),
321      'region/usa/congressional/' + iso.replace('US-', '').toLowerCase(),
322      'region/usa/congressional2022/' + iso.replace('US-', '').toLowerCase(),
323    ];
324  } else if ('GB' === iso) {
325    tempIso = ['uk', 'ukCountries', 'ukCounties'];
326  } else if ('BA' === iso) {
327    tempIso = ['bosniaHerzegovinaCantons', 'bosniaHerzegovina'];
328  } else if ('IL' === iso) {
329    tempIso = ['israel', 'israelPalestine'];
330  } else if ('SI' === iso) {
331    tempIso = ['sloveniaRegions', 'slovenia'];
332  } else if ('IN' === iso) {
333    tempIso = ['india2019', 'india', 'india2020'];
334  } else if ('AE' === iso) {
335    tempIso = ['unitedArabEmirates', 'uae'];
336  } else if ('FR' === iso) {
337    tempIso = ['franceDepartments', 'france'];
338  } else if ('MG' === iso) {
339    tempIso = ['madagascarProvince', 'madagascarRegion'];
340  } else if ('PT' === iso) {
341    tempIso = ['portugal', 'portugalRegions'];
342  } else if ('RS' === iso) {
343    tempIso = ['serbia', 'serbiaNoKosovo'];
344  } else if ('CD' === iso) {
345    tempIso = ['congoDR'];
346  } else if ('CG' === iso) {
347    tempIso = ['congo'];
348  } else if ('CZ' === iso) {
349    tempIso = ['czechRepublic', 'czechia'];
350  } else if ('MM' === iso) {
351    tempIso = ['myanmar'];
352  } // the rest
353
354  // this block needs reviewing.. if/else are a mess
355  if (Array.isArray(tempIso)) {
356    tempIso.forEach(function (item) {
357      if (series.hasOwnProperty(item)) {
358        match = item;
359      }
360    });
361
362    if (match) {
363      return match;
364    } else {
365      if (countries.hasOwnProperty(iso)) {
366        return iMapsRouter.camelize(countries[iso]);
367      }
368    }
369  } else {
370    if (countries.hasOwnProperty(iso)) {
371      return iMapsRouter.camelize(countries[iso]);
372    }
373  }
374  return false;
375};
376
377iMapsRouter.getCountries = function () {
378  var rawjson =
379    '{"AF":"Afghanistan","AX":"\xC5land Islands","AL":"Albania","DZ":"Algeria","AS":"American Samoa","AD":"Andorra","AO":"Angola","AI":"Anguilla","AQ":"Antarctica","AG":"Antigua & Barbuda","AR":"Argentina","AM":"Armenia","AW":"Aruba","AC":"Ascension Island","AU":"Australia","AT":"Austria","AZ":"Azerbaijan","BS":"Bahamas","BH":"Bahrain","BD":"Bangladesh","BB":"Barbados","BY":"Belarus","BE":"Belgium","BZ":"Belize","BJ":"Benin","BM":"Bermuda","BT":"Bhutan","BO":"Bolivia","BA":"Bosnia & Herzegovina","BW":"Botswana","BR":"Brazil","IO":"British Indian Ocean Territory","VG":"British Virgin Islands","BN":"Brunei","BG":"Bulgaria","BF":"Burkina Faso","BI":"Burundi","KH":"Cambodia","CM":"Cameroon","CA":"Canada","IC":"Canary Islands","CV":"Cape Verde","BQ":"Caribbean Netherlands","KY":"Cayman Islands","CF":"Central African Republic","EA":"Ceuta & Melilla","TD":"Chad","CL":"Chile","CN":"China","CX":"Christmas Island","CC":"Cocos (Keeling) Islands","CO":"Colombia","KM":"Comoros","CG":"Congo - Brazzaville","CD":"Congo - Kinshasa","CK":"Cook Islands","CR":"Costa Rica","CI":"C\xF4te d\u2019Ivoire","HR":"Croatia","CU":"Cuba","CW":"Cura\xE7ao","CY":"Cyprus","CZ":"Czechia","DK":"Denmark","DG":"Diego Garcia","DJ":"Djibouti","DM":"Dominica","DO":"Dominican Republic","EC":"Ecuador","EG":"Egypt","SV":"El Salvador","GQ":"Equatorial Guinea","ER":"Eritrea","EE":"Estonia","SZ":"Eswatini","ET":"Ethiopia","FK":"Falkland Islands","FO":"Faroe Islands","FJ":"Fiji","FI":"Finland","FR":"France","GF":"French Guiana","PF":"French Polynesia","TF":"French Southern Territories","GA":"Gabon","GM":"Gambia","GE":"Georgia","DE":"Germany","GH":"Ghana","GI":"Gibraltar","GR":"Greece","GL":"Greenland","GD":"Grenada","GP":"Guadeloupe","GU":"Guam","GT":"Guatemala","GG":"Guernsey","GN":"Guinea","GW":"Guinea-Bissau","GY":"Guyana","HT":"Haiti","HN":"Honduras","HK":"Hong Kong SAR China","HU":"Hungary","IS":"Iceland","IN":"India","ID":"Indonesia","IR":"Iran","IQ":"Iraq","IE":"Ireland","IM":"Isle of Man","IL":"Israel","IT":"Italy","JM":"Jamaica","JP":"Japan","JE":"Jersey","JO":"Jordan","KZ":"Kazakhstan","KE":"Kenya","KI":"Kiribati","XK":"Kosovo","KW":"Kuwait","KG":"Kyrgyzstan","LA":"Laos","LV":"Latvia","LB":"Lebanon","LS":"Lesotho","LR":"Liberia","LY":"Libya","LI":"Liechtenstein","LT":"Lithuania","LU":"Luxembourg","MO":"Macao SAR China","MG":"Madagascar","MW":"Malawi","MY":"Malaysia","MV":"Maldives","ML":"Mali","MT":"Malta","MH":"Marshall Islands","MQ":"Martinique","MR":"Mauritania","MU":"Mauritius","YT":"Mayotte","MX":"Mexico","FM":"Micronesia","MD":"Moldova","MC":"Monaco","MN":"Mongolia","ME":"Montenegro","MS":"Montserrat","MA":"Morocco","MZ":"Mozambique","MM":"Myanmar (Burma)","NA":"Namibia","NR":"Nauru","NP":"Nepal","NL":"Netherlands","NC":"New Caledonia","NZ":"New Zealand","NI":"Nicaragua","NE":"Niger","NG":"Nigeria","NU":"Niue","NF":"Norfolk Island","KP":"North Korea","MK":"North Macedonia","MP":"Northern Mariana Islands","NO":"Norway","OM":"Oman","PK":"Pakistan","PW":"Palau","PS":"Palestinian Territories","PA":"Panama","PG":"Papua New Guinea","PY":"Paraguay","PE":"Peru","PH":"Philippines","PN":"Pitcairn Islands","PL":"Poland","PT":"Portugal","XA":"Pseudo-Accents","XB":"Pseudo-Bidi","PR":"Puerto Rico","QA":"Qatar","RE":"R\xE9union","RO":"Romania","RU":"Russia","RW":"Rwanda","WS":"Samoa","SM":"San Marino","ST":"S\xE3o Tom\xE9 & Pr\xEDncipe","SA":"Saudi Arabia","SN":"Senegal","RS":"Serbia","SC":"Seychelles","SL":"Sierra Leone","SG":"Singapore","SX":"Sint Maarten","SK":"Slovakia","SI":"Slovenia","SB":"Solomon Islands","SO":"Somalia","ZA":"South Africa","GS":"South Georgia & South Sandwich Islands","KR":"South Korea","SS":"South Sudan","ES":"Spain","LK":"Sri Lanka","BL":"St Barth\xE9lemy","SH":"St Helena","KN":"St Kitts & Nevis","LC":"St Lucia","MF":"St Martin","PM":"St Pierre & Miquelon","VC":"St Vincent & Grenadines","SD":"Sudan","SR":"Suriname","SJ":"Svalbard & Jan Mayen","SE":"Sweden","CH":"Switzerland","SY":"Syria","TW":"Taiwan","TJ":"Tajikistan","TZ":"Tanzania","TH":"Thailand","TL":"Timor-Leste","TG":"Togo","TK":"Tokelau","TO":"Tonga","TT":"Trinidad & Tobago","TA":"Tristan da Cunha","TN":"Tunisia","TR":"Turkey","TM":"Turkmenistan","TC":"Turks & Caicos Islands","TV":"Tuvalu","UG":"Uganda","UA":"Ukraine","AE":"United Arab Emirates","GB":"United Kingdom","US":"United States","UY":"Uruguay","UM":"US Outlying Islands","VI":"US Virgin Islands","UZ":"Uzbekistan","VU":"Vanuatu","VA":"Vatican City","VE":"Venezuela","VN":"Vietnam","WF":"Wallis & Futuna","EH":"Western Sahara","YE":"Yemen","ZM":"Zambia","ZW":"Zimbabwe"}';
380  return JSON.parse(rawjson);
381};
382
383iMapsRouter.camelize = function (str) {
384  return str
385    .replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) {
386      return index == 0 ? word.toLowerCase() : word.toUpperCase();
387    })
388    .replace(/\s+/g, '');
389};
390/**
391 * File that contains functions to prepare raw data that arrives in a javascript variable.
392 * 1) Convert color strings to color objects
393 * */
394
395var iMapsModel = {};
396
397iMapsModel.prepareData = function (fullData) {
398  if (!fullData) {
399    return fullData;
400  }
401
402  if (!Array.isArray(fullData)) {
403    return fullData;
404  }
405
406  fullData.forEach(function (data, index) {
407    fullData[index] = iMapsModel.iterateData(data);
408
409    if (Array.isArray(fullData[index].overlay) && fullData[index].overlay.length > 0) {
410      fullData[index].overlay.forEach(function (odata, ind) {
411        // if the overlay map is the same as base map, only include the active regions
412        if (
413          fullData[index].overlay[ind].map === fullData[index].map &&
414          !iMapsManager.bool(fullData[index].allowEmpty)
415        ) {
416          fullData[index].overlay[ind].include = iMapsModel.prepareOverlayInclude(odata);
417        }
418
419        fullData[index].overlay[ind] = iMapsModel.iterateData(odata);
420      });
421    }
422  });
423  return fullData;
424};
425
426iMapsModel.prepareOverlayInclude = function (data) {
427  var includes = data.includes || '';
428
429  if (data.regions) {
430    data.regions.forEach(function (region) {
431      includes += ',' + region.id;
432    });
433  }
434
435  return includes;
436};
437
438iMapsModel.iterateData = function (data) {
439  // check if there's a custom callback function to prepare the data
440  /* eslint-disable-next-line camelcase -- public API: site developers may define a global `igm_custom_filter` function to hook into map data before rendering. */
441  if (typeof igm_custom_filter === 'function') {
442    data = igm_custom_filter(data);
443  } // check if there's a map specific custom callback function
444
445  var mapFunction = 'igm_custom_filter_' + data.id;
446
447  if (typeof window[mapFunction] === 'function') {
448    data = window[mapFunction](data);
449  } // create color objects
450
451  data = iMapsModel.prepareColor(data);
452
453  // convert exclude and includes to array
454  data = iMapsModel.prepareExcludeIncludes(data);
455
456  // convert coordinates to int
457  data = iMapsModel.coordinatesToInt(data);
458
459  // prepare entries that use defaults
460  data = iMapsModel.prepareEntriesData(data);
461
462  // prepare grouped regions
463  data = iMapsModel.prepareGroupedRegions(data);
464
465  //extract image source from imageMarkers
466  data = iMapsModel.prepareImageFields(data);
467
468  //prepare lines multiGeoLine format
469  data = iMapsModel.prepareMultiGeoLine(data);
470  return data;
471};
472
473iMapsModel.prepareMultiGeoLine = function (data) {
474  if (Array.isArray(data.lines) && data.lines.length) {
475    //make sure we only get entries with geoline data
476    data.lines = data.lines.filter(function (line) {
477      if (Array.isArray(line.multiGeoLine) && line.multiGeoLine.length) {
478        return line;
479      }
480    });
481
482    data.lines.map(function (line) {
483      // notice the arrow option needs to be reversed, because it controls the arrow.disabled option when rendered
484      line.arrow = typeof line.arrow !== 'undefined' ? iMapsManager.bool(line.arrow) : false;
485      line.arrowDisabled = !line.arrow;
486      line.multiGeoLine.map(function (geoLine) {
487        Object.assign(geoLine, geoLine.coordinates);
488        delete geoLine.coordinates;
489      });
490    });
491  }
492
493  return data;
494};
495
496iMapsModel.prepareImageFields = function (data) {
497  // image markers
498  if (Array.isArray(data.imageMarkers) && Array.isArray(data.imageMarkers)) {
499    data.imageMarkers.map(function (marker) {
500      if (
501        typeof marker.href === 'undefined' &&
502        marker.image &&
503        (Array.isArray(marker.image) || typeof marker.image === 'object')
504      ) {
505        marker.href = marker.image.url;
506      }
507      return marker;
508    });
509  }
510
511  return data;
512};
513
514iMapsModel.prepareGroupedRegions = function (data) {
515  var regions, tempRegion, group, groupIds, groupName; // regions
516
517  if (Array.isArray(data.regions) && data.regions.length) {
518    if (typeof data.regionGroups === 'undefined') {
519      data.regionGroups = [];
520    }
521
522    // if the option to group entries is enabled, cosider them as a group entry
523    if (iMapsManager.bool(data.regionsGroupHover)) {
524      data.regionGroups.push(data.regions);
525      return data;
526    }
527
528    data.regions.map(function (region, index) {
529      if (region.id && String(region.id).includes(',')) {
530        group = [];
531        groupIds = [];
532        regions = region.id.split(',');
533        groupName = region.name;
534
535        regions.forEach(function (reg) {
536          tempRegion = Object.assign({}, region);
537          tempRegion.id = reg.trim();
538
539          // keep some original values
540          tempRegion.originalID = region.id;
541          tempRegion.groupName = groupName;
542
543          // delete the group name, to keep region name
544          delete tempRegion.name;
545          delete region.name;
546
547          // check if numeric
548          if (!isNaN(tempRegion.id)) {
549            // tempRegion.id = parseFloat( tempRegion.id );
550          }
551
552          // check if not repeated
553          if (!groupIds.includes(tempRegion.id)) {
554            groupIds.push(tempRegion.id);
555            group.push(tempRegion);
556          }
557        });
558
559        // add group to main data object
560        // finally add it to the main data object
561        data.regionGroups.push(group);
562
563        // remove this region from the default regions data set
564        data.regions.splice(index, -1);
565      } else {
566        return region;
567      }
568    });
569  }
570
571  return data;
572};
573
574iMapsModel.prepareTooltip = function (tooltipContent, options) {
575  var maxWidth, isMSIE, images, tooltipNode, range, tempDiv;
576
577  if (typeof options === 'undefined' || typeof tooltipContent === 'undefined') {
578    return tooltipContent;
579  }
580
581  maxWidth =
582    typeof options.maxWidth !== 'undefined' && options.maxWidth !== ''
583      ? parseInt(options.maxWidth)
584      : false;
585  isMSIE = iMapsModel.isMSIE(); // remove html tags for IE
586
587  if (isMSIE) {
588    return iMapsModel.removeHTMLtags(tooltipContent);
589  } // check for images
590
591  if (tooltipContent && tooltipContent.includes('<img')) {
592    // images
593    range = document.createRange(); // Make the parent of the first div in the document becomes the context node
594
595    range.selectNode(document.getElementsByTagName('div').item(0));
596    tooltipNode = range.createContextualFragment(tooltipContent);
597    images = tooltipNode.querySelectorAll('img');
598
599    if (images.length > 0) {
600      images.forEach(function (img) {
601        if (img.width !== 0 && img.style.width === '') {
602          img.style.width = img.width + 'px';
603        }
604
605        if (img.height !== 0 && img.style.height === '') {
606          img.style.height = img.height + 'px';
607        }
608      });
609      tempDiv = document.createElement('div');
610      tempDiv.appendChild(tooltipNode.cloneNode(true));
611      tooltipContent = tempDiv.innerHTML;
612    }
613  } // tooltip
614
615  if (maxWidth && tooltipContent !== '' && !isMSIE) {
616    tooltipContent =
617      '<div class="imapsInnerTooltip" style="max-width:' +
618      maxWidth +
619      'px">' +
620      tooltipContent +
621      '</div>';
622  }
623
624  return tooltipContent;
625};
626
627iMapsModel.isMSIE = function () {
628  return window.navigator.userAgent.match(/(MSIE|Trident)/);
629};
630
631iMapsModel.removeHTMLtags = function (str) {
632  if (str === null || str === '') {
633    return '';
634  } else {
635    str = str.toString();
636  }
637  return str.replace(/<[^>]*>/g, '');
638};
639
640iMapsModel.prepareEntriesData = function (data) {
641  if (data.heatMapMarkers && data.heatMapMarkers.enabled === '1') {
642    data.heatMapMarkers.minRadius = parseInt(data.heatMapMarkers.minRadius);
643    data.heatMapMarkers.maxRadius = parseInt(data.heatMapMarkers.maxRadius);
644  }
645
646  var mapID = data.id; // regions
647
648  if (Array.isArray(data.regions) && data.regions.length) {
649    data.regions.map(function (region) {
650      if (typeof region.useDefaults === 'undefined' || region.useDefaults === '1') {
651        Object.assign(region, data.regionDefaults);
652      }
653
654      if (typeof region.action !== 'undefined' && region.action === 'default') {
655        region.action = data.regionDefaults.action;
656      }
657
658      if (
659        typeof data.regionDefaults.triggerClickOnHover !== 'undefined' &&
660        data.regionDefaults.triggerClickOnHover === '1'
661      ) {
662        region.triggerClickOnHover = true;
663      }
664
665      if (typeof data.regionActiveState !== 'undefined' && data.regionActiveState.enabled === '1') {
666        region.activeState = true;
667      }
668
669      // auto labels properties
670      if (
671        data.regionLabels &&
672        data.regionLabels.source === 'custom' &&
673        typeof data.regionLabels.customSource !== 'undefined'
674      ) {
675        if (data.regionLabels.customSource.includes('.')) {
676          region.autoLabel = data.regionLabels.customSource.split('.').reduce(function (obj, i) {
677            if (obj[i]) {
678              return obj[i];
679            }
680
681            return 0;
682          }, region);
683        } else {
684          region.autoLabel = region[data.regionLabels.customSource];
685        }
686      }
687
688      if (data.heatMapRegions && data.heatMapRegions.enabled === '1') {
689        // if it includes a reference with dot notation
690        if (data.heatMapRegions.source.includes('.')) {
691          region.heatMapRegionRef = data.heatMapRegions.source.split('.').reduce(function (obj, i) {
692            if (obj[i]) {
693              return obj[i];
694            }
695
696            return 0;
697          }, region);
698        }
699
700        delete region.fill;
701      } // default action
702
703      // border hover - currently doesn't exist
704      if (
705        typeof data.visual.borderColorHover !== 'undefined' &&
706        data.visual.borderColorHover !== data.visual.borderColor
707      ) {
708        region.borderColorHover = data.visual.borderColorHover;
709      } // border width on hover - currently doesn't exist
710
711      if (
712        typeof data.visual.borderWidthHover !== 'undefined' &&
713        data.visual.borderWidthHover !== data.visual.borderWidth
714      ) {
715        region.borderWidthHover = data.visual.borderWidthHover;
716      }
717
718      region.tooltipContent = iMapsModel.prepareTooltip(region.tooltipContent, data.tooltip);
719      region.mapID = mapID;
720
721      // fix for value with "-" minus sign
722      if (region.value && _typeof(region.value) !== undefined && region.value !== '') {
723        region.val = region.value;
724        region.value = parseFloat(region.value);
725      }
726
727      return region;
728    }); // heatmap with dot notation
729    // if it includes a reference with dot notation
730
731    if (data.heatMapRegions && data.heatMapRegions.source.includes('.')) {
732      data.heatMapRegions.source = 'heatMapRegionRef';
733    }
734  } // round markers
735
736  if (Array.isArray(data.roundMarkers) && data.roundMarkers.length) {
737    data.roundMarkers.map(function (marker) {
738      if (typeof marker.useDefaults === 'undefined' || marker.useDefaults === '1') {
739        Object.assign(marker, data.markerDefaults);
740      }
741
742      if (marker.coordinates) {
743        marker.latitude = marker.coordinates.latitude;
744        marker.longitude = marker.coordinates.longitude; //Object.assign(marker, marker.coordinates);
745        //delete marker.coordinates;
746      } // set name to be id
747
748      if (typeof marker.latitude === 'string') {
749        marker.latitude = parseFloat(marker.latitude);
750      }
751      if (typeof marker.longitude === 'string') {
752        marker.longitude = parseFloat(marker.latitude);
753      }
754
755      if (typeof marker.name === 'undefined') {
756        marker.name = marker.id;
757      }
758
759      if (data.roundMarkersMobileSize && parseInt(data.roundMarkersMobileSize) !== 100) {
760        if (window.innerWidth <= 780) {
761          marker.radius =
762            (parseFloat(marker.radius) * parseFloat(data.roundMarkersMobileSize)) / 100;
763        }
764      }
765
766      if (data.heatMapMarkers && data.heatMapMarkers.enabled === '1') {
767        if (!isNaN(marker.value)) {
768          marker.value = parseFloat(marker.value);
769        } // if it includes a reference with dot notation
770
771        if (data.heatMapMarkers.source.includes('.')) {
772          marker.heatMapMarkerRef = data.heatMapMarkers.source.split('.').reduce(function (obj, i) {
773            if (obj[i]) {
774              return obj[i];
775            }
776
777            return 0;
778          }, marker);
779        }
780
781        delete marker.fill;
782        delete marker.radius;
783      }
784
785      // fix for value with "-" minus sign
786      if (marker.value && _typeof(marker.value) !== undefined && marker.value !== '') {
787        marker.val = marker.value;
788        marker.value = parseFloat(marker.value);
789      }
790
791      // default action
792      if (marker.action === 'default') {
793        marker.action = data.markerDefaults.action;
794      }
795
796      if (
797        typeof data.markerDefaults.triggerClickOnHover !== 'undefined' &&
798        data.markerDefaults.triggerClickOnHover === '1'
799      ) {
800        marker.triggerClickOnHover = true;
801      }
802
803      if (
804        typeof data.triggerRegionHover !== 'undefined' &&
805        data.triggerRegionHover.enabled === '1'
806      ) {
807        marker.triggerRegionHover = true;
808      }
809
810      marker.tooltipContent = iMapsModel.prepareTooltip(marker.tooltipContent, data.tooltip);
811      marker.mapID = mapID;
812      return marker;
813    }); // remove markers with empty coordinates
814
815    data.roundMarkers = data.roundMarkers.filter(function (marker) {
816      return typeof marker.latitude !== 'undefined' && marker.latitude !== '' && marker.name !== '';
817    }); // heatmap with dot notation
818    // if it includes a reference with dot notation
819
820    if (data.heatMapMarkers && data.heatMapMarkers.source.includes('.')) {
821      data.heatMapMarkers.source = 'heatMapMarkerRef';
822    }
823  } // icon markers
824
825  if (Array.isArray(data.iconMarkers) && data.iconMarkers.length) {
826    data.iconMarkers.map(function (marker) {
827      if (typeof marker.useDefaults === 'undefined' || marker.useDefaults === '1') {
828        Object.assign(marker, data.iconMarkerDefaults);
829      }
830
831      if (marker.coordinates) {
832        marker.latitude = marker.coordinates.latitude;
833        marker.longitude = marker.coordinates.longitude; //Object.assign(marker, marker.coordinates);
834        //delete marker.coordinates;
835      } // set name to be id
836
837      if (typeof marker.name === 'undefined') {
838        marker.name = marker.id;
839      } // default action
840
841      if (marker.action === 'default') {
842        marker.action = data.iconMarkerDefaults.action;
843      }
844
845      if (
846        typeof data.iconMarkerDefaults.triggerClickOnHover !== 'undefined' &&
847        data.iconMarkerDefaults.triggerClickOnHover === '1'
848      ) {
849        marker.triggerClickOnHover = true;
850      }
851
852      // Possible approach to resize icon marker on smaller screens, but won't resize on the fly
853
854      if (data.iconMarkersMobileSize && parseInt(data.iconMarkersMobileSize) !== 100) {
855        if (window.innerWidth <= 780) {
856          marker.scale = (parseFloat(marker.scale) * parseFloat(data.iconMarkersMobileSize)) / 100;
857        }
858      }
859
860      // solve issue with value parameter only accepting numbers
861      marker.val = marker.value;
862      marker.value = parseFloat(marker.value);
863
864      marker.tooltipContent = iMapsModel.prepareTooltip(marker.tooltipContent, data.tooltip);
865      marker.mapID = mapID;
866      return marker;
867    });
868  } // image markers
869
870  if (Array.isArray(data.imageMarkers) && data.imageMarkers.length) {
871    data.imageMarkers.map(function (marker) {
872      if (typeof marker.useDefaults === 'undefined' || marker.useDefaults === '1') {
873        Object.assign(marker, data.imageMarkerDefaults);
874      }
875
876      if (marker.coordinates) {
877        marker.latitude = marker.coordinates.latitude;
878        marker.longitude = marker.coordinates.longitude; //Object.assign(marker, marker.coordinates);
879        //delete marker.coordinates;
880      } // set name to be id
881
882      if (typeof marker.nonScaling !== 'undefined') {
883        marker.nonScaling = iMapsManager.bool(marker.nonScaling);
884      }
885
886      if (typeof marker.size === 'undefined') {
887        marker.size = data.imageMarkerDefaults.size;
888      }
889
890      if (typeof marker.horizontalCenter === 'undefined') {
891        marker.horizontalCenter = data.imageMarkerDefaults.horizontalCenter;
892      }
893
894      if (typeof marker.verticalCenter === 'undefined') {
895        marker.verticalCenter = data.imageMarkerDefaults.verticalCenter;
896      }
897
898      if (typeof marker.name === 'undefined') {
899        marker.name = marker.id;
900      }
901
902      if (marker.action === 'default' || typeof marker.action === 'undefined') {
903        marker.action = data.imageMarkerDefaults.action;
904      }
905
906      if (
907        typeof data.imageMarkerDefaults.triggerClickOnHover !== 'undefined' &&
908        data.imageMarkerDefaults.triggerClickOnHover === '1'
909      ) {
910        marker.triggerClickOnHover = true;
911      }
912
913      // Possible approach to resize icon marker on smaller screens, but won't resize on the fly
914
915      if (data.imageMarkersMobileSize && parseInt(data.imageMarkersMobileSize) !== 100) {
916        if (window.innerWidth <= 780) {
917          marker.size = (parseFloat(marker.size) * parseFloat(data.imageMarkersMobileSize)) / 100;
918        }
919      }
920
921      // solve problem of value param only accepting numbers
922      marker.val = marker.value;
923      marker.value = parseFloat(marker.value);
924
925      marker.tooltipContent = iMapsModel.prepareTooltip(marker.tooltipContent, data.tooltip);
926      marker.mapID = mapID;
927      return marker;
928    });
929  } // labels
930
931  if (Array.isArray(data.labels) && data.labels.length) {
932    data.labels.map(function (label) {
933      if (typeof label.useDefaults === 'undefined' || label.useDefaults === '1') {
934        Object.assign(label, data.labelDefaults);
935        Object.assign(label, data.labelPosition);
936      }
937
938      if (label.coordinates) {
939        Object.assign(label, label.coordinates);
940        delete label.coordinates;
941      }
942
943      if (label.action === 'default') {
944        label.action = data.labelDefaults.action;
945      }
946
947      if (
948        typeof data.labelDefaults.triggerClickOnHover !== 'undefined' &&
949        data.labelDefaults.triggerClickOnHover === '1'
950      ) {
951        label.triggerClickOnHover = true;
952      }
953
954      if (typeof label.size !== 'undefined') {
955        label.fontSize = label.size;
956      } // change font size on smaller screens
957
958      if (data.labelsMobileSize && parseInt(data.labelsMobileSize) !== 100) {
959        if (window.innerWidth <= 780) {
960          label.fontSize = (parseInt(label.fontSize) * parseInt(data.labelsMobileSize)) / 100;
961          label.size = label.fontSize;
962        }
963      }
964
965      label.tooltipContent = iMapsModel.prepareTooltip(label.tooltipContent, data.tooltip);
966      label.mapID = mapID;
967      return label;
968    });
969  } // lines
970
971  if (Array.isArray(data.lines) && data.lines.length) {
972    data.lines.map(function (line) {
973      if (typeof line.useDefaults === 'undefined' || line.useDefaults === '1') {
974        Object.assign(line, data.lineDefaults);
975      }
976
977      line.curvature = parseFloat(line.curvature);
978      return line;
979    });
980  }
981
982  return data;
983};
984
985iMapsModel.prepareExcludeIncludes = function (data) {
986  if (typeof data.onlyIncludeActive !== 'undefined' && parseInt(data.onlyIncludeActive) === 1) {
987    data.include = [];
988    data.exclude = [];
989
990    if (data.regions) {
991      data.regions.forEach(function (region) {
992        data.include.push(region.id);
993
994        if (!isNaN(region.id)) {
995          data.include.push(parseInt(region.id));
996        }
997      });
998    }
999
1000    return data;
1001  }
1002
1003  if (data.exclude && typeof data.exclude === 'string' && data.exclude.trim().length) {
1004    data.exclude = data.exclude.split(',').map(function (item) {
1005      return item.trim();
1006    });
1007  }
1008
1009  if (data.include && data.include.trim().length) {
1010    data.include = data.include.split(',').map(function (item) {
1011      return item.trim();
1012    });
1013
1014    data.include.map(function (item) {
1015      if (!isNaN(item)) {
1016        data.include.push(parseInt(item));
1017      }
1018    });
1019  }
1020
1021  return data;
1022};
1023
1024iMapsModel.coordinatesToInt = function (data) {
1025  var convertCoordinates = function convertCoordinates(key, obj) {
1026      obj[key].latitude = Number(obj[key].latitude);
1027      obj[key].longitude = Number(obj[key].longitude);
1028    },
1029    iterateObj = function iterateObj(Obj) {
1030      if (_typeof(Obj) !== 'object' || Obj === null) {
1031        return;
1032      }
1033
1034      Object.keys(Obj).map(function (key) {
1035        if (_typeof(Obj[key]) === 'object') {
1036          if (key === 'coordinates' || key === 'homeGeoPoint') {
1037            convertCoordinates(key, Obj);
1038          } else if (typeof Obj['className'] !== 'undefined') {
1039            return;
1040          } else {
1041            iterateObj(Obj[key]);
1042          }
1043        }
1044      });
1045    };
1046
1047  iterateObj(data);
1048  return data;
1049};
1050
1051iMapsModel.prepareColor = function (data) {
1052  // prepare color fields
1053  var colorFields = [
1054      'inactiveColor',
1055      'activeColor',
1056      'hoverColor',
1057      'hover',
1058      'inactiveHoverColor',
1059      'backgroundColor',
1060      'color',
1061      'minColor',
1062      'maxColor',
1063      'fill',
1064      'projectionBackgroundColor',
1065      'borderColor',
1066      'borderColorHover',
1067    ],
1068    createGradient = function createGradient(data) {
1069      var colours = data.split('|');
1070      var gradient, gradientType, gradientOff
1070set;
1071
1072      gradientType = typeof igmGradientType !== 'undefined' ? igmGradientType : 'LinearGradient';
1073      gradientOffset =
1074        typeof igmGradientOffset !== 'undefined' && Array.isArray(igmGradientOffset)
1075          ? igmGradientOffset
1076          : [];
1077
1078      gradient = new am4core[gradientType]();
1079      colours.forEach(function (color, index) {
1080        gradient.addColor(am4core.color(color), 1, gradientOffset[index]);
1081      });
1082
1083      //rotation
1084      gradient.rotation = typeof igmGradientRotation !== 'undefined' ? igmGradientRotation : 0;
1085      return gradient;
1086    },
1087    createPattern = function createPattern(data) {
1088      var url = data;
1089      // Create pattern
1090      var pattern = new am4core.Pattern();
1091      pattern.width = 150;
1092      pattern.height = 150;
1093      pattern.strokeWidth = 0;
1094      pattern.stroke = am4core.color('#6699cc');
1095      //pattern.patternUnits = 'objectBoundingBox'; // objectBoundingBox // userSpaceOnUse
1096
1097      var image = new am4core.Image();
1098      image.href = url;
1099      image.width = 150;
1100      image.height = 150;
1101      image.x = 0;
1102      image.y = 0;
1103      image.verticalCenter = 'middle';
1104      image.valign = 'middle';
1105      pattern.addElement(image.element);
1106      pattern.addElement(image.element);
1107      pattern.addElement(image.element);
1108      return pattern;
1109    },
1110    checkColor = function checkColor(key, obj) {
1111      if (colorFields.includes(key)) {
1112        if (obj[key].includes('|')) {
1113          obj[key] = createGradient(obj[key]);
1114        } else if (obj[key].startsWith('http')) {
1115          obj[key] = createPattern(obj[key]);
1116        }
1117        // on hover out, amcharts is not able to resolve transparent colours, in default states, so we prevent this
1118        // by setting an almost transparent colour
1119        else if (key === 'hover' && (obj[key] === 'transparent' || obj[key].endsWith(',0)'))) {
1120          obj[key] = am4core.color('rgba(0,0,0,0.001)');
1121        } else if (obj[key] === 'transparent') {
1122          obj[key] = am4core.color('rgba(0,0,0,0)');
1123        } else {
1124          obj[key] = am4core.color(obj[key]);
1125        }
1126      }
1127    },
1128    iterateObj = function iterateObj(Obj) {
1129      if (_typeof(Obj) !== 'object' || Obj === null) {
1130        return;
1131      }
1132
1133      Object.keys(Obj).map(function (key) {
1134        if (_typeof(Obj[key]) === 'object') {
1135          iterateObj(Obj[key]);
1136        } else if (typeof Obj['className'] !== 'undefined') {
1137          return;
1138        } else {
1139          checkColor(key, Obj);
1140        }
1141      });
1142    };
1143
1144  iterateObj(data);
1145  return data;
1146};
1147
1148/**
1149 * Retrieves object with region codes and names from geojson
1150 */
1151
1152iMapsModel.extractCodes = function (data) {
1153  var obj = {};
1154
1155  for (var i = 0; i < data.features.length; i++) {
1156    obj[data.features[i].properties.id] = data.features[i].properties.name;
1157  }
1158
1159  return obj;
1160};
1161
1162('use strict');
1163/* jshint node: true */
1164
1165/* global Promise */
1166
1167/* jshint browser: true */
1168
1169/* global am4maps */
1170
1171/* global am4core */
1172
1173/* global iMapsData */
1174
1175/* global geocluster */
1176
1177/* global iMapsRouter */
1178
1179/* global iMaps */
1180
1181/* jshint esversion:5 */
1182
1183/* global iMapsActions */
1184
1185/*
1186Model Object with methods and helpers to build the maps
1187*/
1188
1189var iMapsManager = {};
1190iMapsManager.maps = {};
1191
1192iMapsManager.init = function (i) {
1193  var im = this;
1194  im.addMap(i);
1195};
1196/**
1197 * Adds maps based on data index
1198 *
1199 */
1200
1201iMapsManager.addMap = function (index) {
1202  var im = this,
1203    data = iMapsData.data[index],
1204    id = data.id,
1205    map,
1206    regionSeries,
1207    groupedSeries,
1208    markerSeries,
1209    labelSeries,
1210    customLegend,
1211    imageSeries,
1212    iconSeries,
1213    graticuleSeries,
1214    clusterSeries,
1215    mapVar,
1216    bgSeries,
1217    bgImage,
1218    aspRatioContainer,
1219    container = document.getElementById(data.container);
1220
1221  if (data.disabled) {
1222    return;
1223  }
1224
1225  if (container === null) {
1226    return;
1227  }
1228
1229  aspRatioContainer = container.closest('.map_aspect_ratio');
1230
1231  // if map was already built
1232  if (typeof im.maps[id] !== 'undefined') {
1233    im.maps[id].map.dispose();
1234  }
1235
1236  // map container size adjustment
1237  // if mobile
1238  if (
1239    window.innerWidth <= 780 &&
1240    typeof data.visual.paddingTop !== 'undefined' &&
1241    data.visual.paddingTop !== ''
1242  ) {
1243    aspRatioContainer.style.paddingTop = String(data.visual.paddingTopMobile) + '%';
1244  } else {
1245    aspRatioContainer.style.paddingTop = String(data.visual.paddingTop) + '%';
1246  }
1247
1248  window.addEventListener('resize', function () {
1249    if (
1250      window.innerWidth <= 780 &&
1251      typeof data.visual.paddingTop !== 'undefined' &&
1252      data.visual.paddingTop !== ''
1253    ) {
1254      aspRatioContainer.style.paddingTop = String(data.visual.paddingTopMobile) + '%';
1255    } else {
1256      aspRatioContainer.style.paddingTop = String(data.visual.paddingTop) + '%';
1257    }
1258  });
1259
1260  if (data.visual.maxWidth !== '') {
1261    //container.closest(".map_box").style.maxWidth = String(data.visual.maxWidth) + "px";
1262  }
1263
1264  if (data.visual.fontFamily !== '' && data.visual.fontFamily !== 'inherit') {
1265    container.closest('.map_box').style.fontFamily = data.visual.fontFamily;
1266  }
1267
1268  // create map and a shorter reference to it
1269  im.maps[id] = {
1270    map: am4core.create(data.container, am4maps.MapChart),
1271    series: [],
1272    clusterSeries: {},
1273    seriesIndex: {},
1274    seriesById: {},
1275    data: data,
1276    allBaseSeries: [],
1277    labelSeries: [],
1278    baseRegionSeries: {},
1279    groupedBaseRegionSeries: [],
1280    backgroundSeries: {},
1281  };
1282  map = im.maps[id].map;
1283  map.readerTitle = 'Interactive Map';
1284
1285  clusterSeries = im.maps[id].clusterSeries;
1286  // ready event
1287  // on click map debug
1288  map.events.on('ready', function () {
1289    var event = new Event('mapready');
1290    container.dispatchEvent(event);
1291    // we might move the event that triggers on ready to mapappeared because it doesn't work well with custom maps
1292    im.triggerOnReady(id, data);
1293  });
1294  map.events.on('appeared', function () {
1295    var event = new Event('mapappeared');
1296    container.dispatchEvent(event);
1297    im.triggerOnAppeared(id, data);
1298    container.classList.remove('map_loading');
1299  });
1300
1301  // locale
1302  if (
1303    typeof iMapsData.options !== 'undefined' &&
1304    typeof iMapsData.options.locale !== 'undefined' &&
1305    iMapsData.options.locale &&
1306    typeof window['am4lang_' + iMapsData.options.locale] !== 'undefined'
1307  ) {
1308    map.language.locale = window['am4lang_' + iMapsData.options.locale];
1309  } // enable small map
1310
1311  if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) {
1312    map.smallMap = new am4maps.SmallMap();
1313  } // load map geodata
1314
1315  if (data.map === 'custom' || im.bool(data.useGeojson)) {
1316    map.geodataSource.url = data.mapURL;
1317  } else {
1318    mapVar = iMapsRouter.getVarByName(data.map);
1319    map.geodata = window[mapVar];
1320  }
1321
1322  if (
1323    typeof iMapsData.options !== 'undefined' &&
1324    typeof iMapsData.options.lang !== 'undefined' &&
1325    iMapsData.options.lang &&
1326    typeof window['am4geodata_lang_' + iMapsData.options.lang] !== 'undefined'
1327  ) {
1328    map.geodataNames = window['am4geodata_lang_' + iMapsData.options.lang];
1329  }
1330
1331  // projection - moved to the end of the function to fix issue with Albers not rendering correctly
1332  map.projection = new am4maps.projections[data.projection]();
1333
1334  // fix issues with USA territories map
1335  if (data.map.startsWith('usaTerritories')) {
1336    map.events.on('ready', function () {
1337      map.projection = new am4maps.projections[data.projection]();
1338    });
1339  }
1340
1341  map.fontFamily = data.visual.fontFamily;
1342
1343  // export menu
1344  if (data.exportMenu && im.bool(data.exportMenu.enable)) {
1345    map.exporting.menu = new am4core.ExportMenu();
1346    map.exporting.menu.items[0].icon =
1347      'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiA8Zz4KICA8dGl0bGU+YmFja2dyb3VuZDwvdGl0bGU+CiAgPHJlY3QgeD0iLTEiIHk9Ii0xIiB3aWR0aD0iNS45NDQ3NzMiIGhlaWdodD0iNS45NDQ3NzMiIGlkPSJjYW52YXNfYmFja2dyb3VuZCIgZmlsbD0ibm9uZSIvPgogPC9nPgoKIDxnPgogIDx0aXRsZT5MYXllciAxPC90aXRsZT4KICA8cGF0aCBzdHJva2U9Im51bGwiIGQ9Im0xNC40MjcwMzEsMTUuNzQwNDAxcTAsLTAuMjU3NjQzIC0wLjE4ODI3NCwtMC40NDU5MTd0LTAuNDQ1OTE3LC0wLjE4ODI3NHQtMC40NDU5MTcsMC4xODgyNzR0LTAuMTg4Mjc0LDAuNDQ1OTE3dDAuMTg4Mjc0LDAuNDQ1OTE3dDAuNDQ1OTE3LDAuMTg4Mjc0dDAuNDQ1OTE3LC0wLjE4ODI3NHQwLjE4ODI3NCwtMC40NDU5MTd6bTIuNTM2NzgsMHEwLC0wLjI1NzY0MyAtMC4xODgyNzQsLTAuNDQ1OTE3dC0wLjQ0NTkxNywtMC4xODgyNzR0LTAuNDQ1OTE3LDAuMTg4Mjc0dC0wLjE4ODI3NCwwLjQ0NTkxN3QwLjE4ODI3NCwwLjQ0NTkxN3QwLjQ0NTkxNywwLjE4ODI3NHQwLjQ0NTkxNywtMC4xODgyNzR0MC4xODgyNzQsLTAuNDQ1OTE3em0xLjI2ODM5LC0yLjIxOTY4NWwwLDMuMTcwOTdxMCwwLjM5NjM3NCAtMC4yNzc0NjEsMC42NzM4MzR0LTAuNjczODM0LDAuMjc3NDYxbC0xNC41ODY0NywwcS0wLjM5NjM3NCwwIC0wLjY3MzgzNCwtMC4yNzc0NjF0LTAuMjc3NDYxLC0wLjY3MzgzNGwwLC0zLjE3MDk3cTAsLTAuMzk2Mzc0IDAuMjc3NDYxLC0wLjY3MzgzNHQwLjY3MzgzNCwtMC4yNzc0NjFsNC42MDc4MTYsMGwxLjMzNzc1MSwxLjM0NzY1OXEwLjU3NDczOCwwLjU1NDkyMSAxLjM0NzY1OSwwLjU1NDkyMXQxLjM0NzY1OSwtMC41NTQ5MjFsMS4zNDc2NTksLTEuMzQ3NjU5bDQuNTk3OTA4LDBxMC4zOTYzNzQsMCAwLjY3MzgzNCwwLjI3NzQ2MXQwLjI3NzQ2MSwwLjY3MzgzNGwwLjAwMDAxOCwwem0tMy4yMjA1MjMsLTUuNjM4MzlxMC4xNjg0NTYsMC40MDYyODIgLTAuMTM4NzMsMC42OTM2NTFsLTQuNDM5MzYsNC40MzkzNnEtMC4xNzgzNjUsMC4xODgyNzQgLTAuNDQ1OTE3LDAuMTg4Mjc0dC0wLjQ0NTkxNywtMC4xODgyNzRsLTQuNDM5MzYsLTQuNDM5MzZxLTAuMzA3MTg3LC0wLjI4NzM2OSAtMC4xMzg3MywtMC42OTM2NTFxMC4xNjg0NTYsLTAuMzg2NDY1IDAuNTg0NjQ3LC0wLjM4NjQ2NWwyLjUzNjc4LDBsMCwtNC40MzkzNnEwLC0wLjI1NzY0MyAwLjE4ODI3NCwtMC40NDU5MTd0MC40NDU5MTcsLTAuMTg4Mjc0bDIuNTM2NzgsMHEwLjI1NzY0MywwIDAuNDQ1OTE3LDAuMTg4Mjc0dDAuMTg4Mjc0LDAuNDQ1OTE3bDAsNC40MzkzNmwyLjUzNjc4LDBxMC40MTYxOTEsMCAwLjU4NDY0NywwLjM4NjQ2NXoiIGlkPSJzdmdfMSIvPgogPC9nPgo8L3N2Zz4=';
1348    map.exporting.menu.align = data.exportMenu.align ? data.exportMenu.align : 'right';
1349    map.exporting.menu.verticalAlign = data.exportMenu.verticalAlign
1350      ? data.exportMenu.verticalAlign
1351      : 'top';
1352  }
1353
1354  // touch devices options
1355  if (data.touchInterface) {
1356    if (im.bool(data.touchInterface.tapToActivate)) {
1357      map.tapToActivate = true;
1358      map.tapTimeout = data.touchInterface.tapTimeout;
1359    }
1360
1361    if (im.bool(data.touchInterface.dragGrip)) {
1362      map.dragGrip.disabled = false;
1363      map.dragGrip.autoHideDelay = data.touchInterface.dragGripAutoHideDelay;
1364    }
1365  }
1366
1367  // different map center
1368  //map.deltaLongitude = -10;
1369  // pan behaviours
1370  // map.panBehavior = "rotateLongLat";
1371  // map.panBehavior = "rotateLong";
1372  // map.deltaLatitude = -20;
1373  // map.padding(20, 20, 20, 20);
1374  // visual settings
1375
1376  map.background.fill = data.visual.backgroundColor;
1377  map.background.fillOpacity = data.visual.backgroundOpacity;
1378
1379  // iOS scroll issue fix - not working
1380  map.backgroundSeries.mapPolygons.template.focusable = false;
1381  map.background.focusable = false;
1382  map.backgroundSeries.focusable = false;
1383
1384  // background image
1385  if (
1386    typeof data.visual.bgImage !== 'undefined' &&
1387    typeof data.visual.bgImage.url !== 'undefined' &&
1388    data.visual.bgImage.url !== ''
1389  ) {
1390    bgSeries = map.series.push(new am4maps.MapImageSeries());
1391    bgSeries.toBack();
1392    bgImage = bgSeries.mapImages.template.createChild(am4core.Image);
1393    bgImage.propertyFields.href = 'src';
1394    bgImage.width = map.width;
1395    bgImage.height = map.height;
1396    bgSeries.data = [
1397      {
1398        src: data.visual.bgImage.url,
1399      },
1400    ];
1401
1402    im.maps[id].backgroundSeries = bgSeries;
1403  }
1404
1405  map.exporting.backgroundColor = data.visual.backgroundColor;
1406  map.exporting.filePrefix = 'interactive_map';
1407  map.exporting.useWebFonts = false;
1408
1409  // graticulate - grid lines
1410  if (data.projection === 'Orthographic' && data.grid) {
1411    graticuleSeries = map.series.push(new am4maps.GraticuleSeries());
1412    graticuleSeries.mapLines.template.line.stroke = data.grid.color;
1413    graticuleSeries.mapLines.template.line.strokeOpacity = 1;
1414    graticuleSeries.fitExtent = false;
1415    map.backgroundSeries.mapPolygons.template.polygon.fillOpacity = 1;
1416    map.backgroundSeries.mapPolygons.template.polygon.fill = data.grid.projectionBackgroundColor;
1417  }
1418
1419  im.handleZoom(id); // Auto Legend
1420
1421  if (data.legend && im.bool(data.legend.enabled)) {
1422    map.legend = new am4maps.Legend(); // positioning
1423
1424    if (data.legend.position === 'top' || data.legend.position === 'bottom') {
1425      map.legend.contentAlign = data.legend.align;
1426      map.legend.valign = data.legend.position;
1427    } else {
1428      map.legend.position = data.legend.position;
1429      map.legend.align = data.legend.position;
1430      map.legend.valign = data.legend.valign;
1431    }
1432
1433    if (typeof data.legend.style !== 'undefined' && data.legend.style !== 'default') {
1434      im.setLegendStyle(id, map.legend, data.legend.style);
1435    }
1436
1437    if (_typeof(data.legend.fill) !== undefined) {
1438      map.legend.labels.template.fill = data.legend.fill;
1439    } // interactive
1440
1441    if (data.legend.clickable === 'false') {
1442      map.legend.itemContainers.template.interactionsEnabled = false;
1443    }
1444
1445    if (data.legend.clickable === 'toggle') {
1446      // do nothing, it's the default event
1447      // let's just clear the events, just in case.
1448      map.legend.itemContainers.template.events.on('hit', function () {
1449        iMapsManager.clearSelected(id);
1450      });
1451    }
1452
1453    if (data.legend.clickable === 'select') {
1454      map.legend.itemContainers.template.togglable = false;
1455      map.legend.itemContainers.template.events.on('hit', function (ev) {
1456        iMapsManager.clearSelected(id);
1457        var select = [];
1458        var seriesType = im.getTargetSeriesType(ev.target.dataItem.dataContext); // currently only works for region series
1459
1460        if (seriesType === 'MapPolygonSeries') {
1461          ev.target.dataItem.dataContext.mapPolygons.each(function (polygon) {
1462            if (!polygon.dataItem.dataContext.madeFromGeoData) {
1463              polygon.setState('active');
1464              polygon.isActive = true;
1465              polygon.isHover = false;
1466              select.push(polygon);
1467            }
1468          });
1469          im.maps[id].selected = select;
1470        }
1471      });
1472      map.legend.itemContainers.template.events.on('over', function (ev) {
1473        var seriesType = im.getTargetSeriesType(ev.target.dataItem.dataContext);
1474        var target = '';
1475
1476        // bring this series to front
1477        ev.target.dataItem.dataContext.zIndex = Number.MAX_VALUE;
1478        ev.target.dataItem.dataContext.toFront();
1479
1480        if (seriesType === 'MapImageSeries') {
1481          target = ev.target.dataItem.dataContext.mapImages;
1482          target.each(function (marker) {
1483            marker.setState('highlight');
1484          });
1485        } else if (seriesType === 'MapPolygonSeries') {
1486          target = ev.target.dataItem.dataContext.mapPolygons;
1487          target.each(function (polygon) {
1488            if (!polygon.dataItem.dataContext.madeFromGeoData) {
1489              polygon.setState('highlight');
1490            }
1491          });
1492        } else {
1493          return;
1494        }
1495      });
1496      map.legend.itemContainers.template.events.on('out', function (ev) {
1497        var seriesType = im.getTargetSeriesType(ev.target.dataItem.dataContext);
1498        var target = '';
1499
1500        if (seriesType === 'MapImageSeries') {
1501          target = ev.target.dataItem.dataContext.mapImages;
1502          target.each(function (marker) {
1503            marker.setState('default');
1504          });
1505        } else if (seriesType === 'MapPolygonSeries') {
1506          target = ev.target.dataItem.dataContext.mapPolygons;
1507          target.each(function (polygon) {
1508            if (!polygon.dataItem.dataContext.madeFromGeoData) {
1509              polygon.setState('default');
1510            }
1511          });
1512        } else {
1513          return;
1514        }
1515      });
1516    }
1517  }
1518
1519  // Custom Legend
1520  if (data.customLegend && im.bool(data.customLegend.enabled)) {
1521    var customLegendType =
1522      typeof data.customLegend.type !== 'undefined' ? data.customLegend.type : 'internal';
1523
1524    if (
1525      data.customLegend.data &&
1526      Array.isArray(data.customLegend.data) &&
1527      customLegendType === 'internal'
1528    ) {
1529      customLegend = new am4maps.Legend();
1530      customLegend.parent = map.chartContainer;
1531      customLegend.data = data.customLegend.data;
1532
1533      /*
1534			customLegend.data.map(function(entry){
1535			  entry.name = entry.name.replace(/&#(\d+);/g, function(match, dec) {
1536			    return String.fromCharCode(dec);
1537			  });
1538			  return entry;
1539			});*/
1540
1541      if (typeof data.customLegend.style !== 'undefined' && data.customLegend.style !== 'default') {
1542        im.setLegendStyle(id, customLegend, data.customLegend.style);
1543      } // no interaction with mouse
1544
1545      customLegend.itemContainers.template.clickable = false;
1546      customLegend.itemContainers.template.focusable = false;
1547      customLegend.itemContainers.template.hoverable = false;
1548      customLegend.itemContainers.template.cursorOverStyle = am4core.MouseCursorStyle['default'];
1549      customLegend.labels.template.truncate = false;
1550      customLegend.labels.template.wrap = true; // positioning
1551
1552      if (data.customLegend.position === 'top' || data.customLegend.position === 'bottom') {
1553        customLegend.contentAlign = data.customLegend.align;
1554        customLegend.valign = data.customLegend.position;
1555      } else {
1556        customLegend.position = data.customLegend.position;
1557        customLegend.align = data.customLegend.position;
1558        customLegend.valign = data.customLegend.valign;
1559      }
1560
1561      if (_typeof(data.customLegend.fill) !== undefined) {
1562        customLegend.labels.template.fill = data.customLegend.fill;
1563      }
1564    }
1565  }
1566
1567  // Create Main Series
1568  regionSeries = im.pushRegionSeries(id, data);
1569  im.maps[id].baseRegionSeries = regionSeries; // Check for grouped regions
1570
1571  if (Array.isArray(data.regionGroups) && data.regionGroups.length) {
1572    groupedSeries = im.pushGroupSeries(id, data);
1573    im.maps[id].groupedBaseRegionSeries = groupedSeries;
1574  } // Overlay collections - we can add all series in the preset order
1575
1576  if (Array.isArray(data.overlay) && data.overlay.length) {
1577    data.overlay.forEach(function (mapObj) {
1578      im.pushSeries(id, mapObj);
1579    });
1580  } // Create Other Series - we create them after the overlay to avoid overlap
1581
1582  if (Array.isArray(data.lines) && data.lines.length) {
1583    im.pushLineSeries(id, data);
1584  }
1585
1586  if (Array.isArray(data.roundMarkers) && data.roundMarkers.length) {
1587    markerSeries = im.pushRoundMarkerSeries(id, data);
1588
1589    if (data.clusterMarkers && im.bool(data.clusterMarkers.enabled)) {
1590      markerSeries.hidden = true;
1591      im.setupClusters(data, id);
1592      clusterSeries[id].zoomLevels[data.clusterMarkers.zoomLevel] = markerSeries;
1593      // we setup the main index series (zoom=1) to be visible
1594      // when doing it inside setupClusters function, there was a bug
1595      if (clusterSeries[id].zoomLevels[1]) {
1596        clusterSeries[id].zoomLevels[1].hidden = false;
1597        // we also add it to the base series here, since it wasn't working in the setupClusters function
1598        im.maps[id].allBaseSeries.push(clusterSeries[id].zoomLevels[1]);
1599      }
1600    }
1601  }
1602
1603  if (Array.isArray(data.imageMarkers) && data.imageMarkers.length) {
1604    imageSeries = im.pushImageMarkerSeries(id, data); //imageSeries.hiddenInLegend = true;
1605
1606    im.maps[id].allBaseSeries.push(imageSeries);
1607  }
1608
1609  if (Array.isArray(data.iconMarkers) && data.iconMarkers.length) {
1610    iconSeries = im.pushIconMarkerSeries(id, data); //iconSeries.hiddenInLegend = true;
1611
1612    im.maps[id].allBaseSeries.push(iconSeries);
1613  }
1614
1615  if (Array.isArray(data.labels) && data.labels.length) {
1616    labelSeries = im.pushLabelSeries(id, data); //labelSeries.hiddenInLegend = true;
1617
1618    im.maps[id].allBaseSeries.push(labelSeries);
1619  } // checks if we should display info and creates events.
1620
1621  im.handleInfoBox(id); // map.projection = new am4maps.projections[data.projection]();
1622};
1623
1624iMapsManager.setLegendStyle = function (id, legend, style) {
1625  var sizes = {
1626    xsmall: 10,
1627    small: 13,
1628    regular: 16,
1629    large: 19,
1630    larger: 23,
1631  };
1632  var legendSize = sizes[style];
1633  var legendMarkerTemplate = legend.markers.template;
1634  legend.labels.template.fontSize = legendSize;
1635  legend.useDefaultMarker = true;
1636  legendMarkerTemplate.width = legendSize;
1637  legendMarkerTemplate.height = legendSize;
1638};
1639
1640iMapsManager.handleZoom = function (id) {
1641  var im = this,
1642    map = im.maps[id].map,
1643    data = im.maps[id].data,
1644    defaultOffset = true,
1645    defaultZoom = true; // Viewport settings
1646
1647  // Zoom Level
1648  if (typeof data.viewport !== 'undefined' && parseFloat(data.viewport.zoomLevel) >= 1) {
1649    map.homeZoomLevel = parseFloat(data.viewport.zoomLevel);
1650    defaultZoom = false; // to make sure everything else is disabled by default
1651
1652    map.seriesContainer.resizable = false;
1653    map.seriesContainer.draggable = false;
1654    map.chartContainer.wheelable = false;
1655  }
1656
1657  // Home center point
1658  if (
1659    typeof data.viewport !== 'undefined' &&
1660    data.viewport.homeGeoPoint &&
1661    data.viewport.homeGeoPoint.latitude !== 0 &&
1662    data.viewport.homeGeoPoint.longitude !== 0
1663  ) {
1664    map.homeGeoPoint = data.viewport.homeGeoPoint;
1665  }
1666
1667  // delta Coordinates Offset
1668  if (typeof data.viewport !== 'undefined' && data.viewport.offset) {
1669    // only change if there are values, otherwise we might mess up projections (Albers)
1670    if (data.viewport.offset.longitude !== '' && data.viewport.offset.longitude !== '0') {
1671      defaultOffset = false;
1672      map.deltaLongitude = data.viewport.offset.longitude;
1673    }
1674
1675    if (data.viewport.offset.latitude !== '' && data.viewport.offset.latitude !== '0') {
1676      defaultOffset = false;
1677      map.deltaLatitude = data.viewport.offset.latitude;
1678    }
1679
1680    if (defaultOffset) {
1681      iMapsManager.latLongOffsetFix(data, map, defaultZoom);
1682    }
1683  }
1684
1685  // manual fixes for NZ and RU and Asia maps
1686  if (typeof data.viewport === 'undefined') {
1687    iMapsManager.latLongOffsetFix(data, map, defaultZoom);
1688  }
1689
1690  // default zoom behaviour
1691  if (typeof data.zoom === 'undefined') {
1692    // default zoom behaviour when we can't find zoom settings
1693    if (typeof data.zoomMaster !== 'undefined' && im.bool(data.zoomMaster)) {
1694      map.seriesContainer.draggable = true;
1695      map.seriesContainer.resizable = true;
1696
1697      // display zoom controls by default
1698      map.zoomControl = new am4maps.ZoomControl();
1699      map.zoomControl.focusable = false;
1700
1701      // iOS fix
1702      map.zoomControl.exportable = false;
1703      if (map.zoomControl.children && map.zoomControl.children.values) {
1704        map.zoomControl.children.values.forEach(function (child) {
1705          child.focusable = false;
1706        });
1707      }
1708
1709      map.zoomControl.align = 'right';
1710      map.zoomControl.valign = 'bottom';
1711    } else {
1712      map.seriesContainer.resizable = false;
1713      map.seriesContainer.draggable = false;
1714    }
1715
1716    map.seriesContainer.events.disableType('doublehit');
1717    map.chartContainer.background.events.disableType('doublehit');
1718    map.chartContainer.wheelable = false;
1719    return;
1720  } // disable drag in Orthographic and leave default for the rest
1721
1722  if (data.projection !== 'Orthographic') {
1723    map.seriesContainer.draggable = data.zoom ? im.bool(data.zoom.draggable) : false;
1724    map.seriesContainer.resizable = data.zoom ? im.bool(data.zoom.draggable) : false; // don't zoom out to center
1725
1726    if (im.bool(data.zoom.draggable)) {
1727      // in case we want to show the grab hand on mousehover also
1728      //map.seriesContainer.cursorOverStyle = am4core.MouseCursorStyle.grab;
1729      map.seriesContainer.cursorDownStyle = am4core.MouseCursorStyle.grabbing;
1730    }
1731
1732    // control zoom and pan behaviour
1733    map.centerMapOnZoomOut = false;
1734    map.maxPanOut = 0;
1735
1736    // zoom is enabled, only allow drag on mobile
1737    if (
1738      im.bool(data.zoom.enabled) &&
1739      !im.bool(data.zoom.draggable) &&
1740      im.bool(data.zoom.mobileResizable) &&
1741      /Mobi|Android/i.test(navigator.userAgent)
1742    ) {
1743      map.seriesContainer.draggable = true;
1744      map.seriesContainer.resizable = true;
1745    }
1746  } else {
1747    map.seriesContainer.draggable = false;
1748    map.seriesContainer.resizable = false;
1749    map.panBehavior = 'rotateLongLat';
1750  } // disable zoom
1751
1752  if (!im.bool(data.zoom.enabled)) {
1753    map.maxZoomLevel = parseFloat(data.viewport.zoomLevel);
1754    map.seriesContainer.events.disableType('doublehit');
1755    map.chartContainer.background.events.disableType('doublehit');
1756    map.seriesContainer.draggable = false;
1757    map.seriesContainer.resizable = false;
1758    map.chartContainer.wheelable = false;
1759  } else {
1760    // mouse wheel zoom
1761    map.chartContainer.wheelable = im.bool(data.zoom.wheelable); // double click zoom
1762
1763    if (!im.bool(data.zoom.doubleHitZoom)) {
1764      map.seriesContainer.events.disableType('doublehit');
1765      map.chartContainer.background.events.disableType('doublehit');
1766    }
1767
1768    if (typeof data.zoom.maxZoomLevel !== 'undefined') {
1769      map.maxZoomLevel = parseInt(data.zoom.maxZoomLevel);
1770    } // Zoom Controls
1771
1772    if (im.bool(data.zoom.controls)) {
1773      map.zoomControl = new am4maps.ZoomControl();
1774
1775      // iOS fix
1776      map.zoomControl.exportable = false;
1777      if (map.zoomControl.children && map.zoomControl.children.values) {
1778        map.zoomControl.children.values.forEach(function (child) {
1779          child.focusable = false;
1780        });
1781      }
1782
1783      map.zoomControl.exportable = false;
1784      map.zoomControl.align = data.zoom.controlsPositions
1785        ? data.zoom.controlsPositions.align
1786        : 'right';
1787      map.zoomControl.valign = data.zoom.controlsPositions
1788        ? data.zoom.controlsPositions.valign
1789        : 'bottom';
1790
1791      if (typeof data.zoom.homeButton === 'undefined' || im.bool(data.zoom.homeButton)) {
1792        // home button
1793        var homeButton = new am4core.Button();
1794        // iOS scroll fix
1795        homeButton.focusable = false;
1796        homeButton.events.on('hit', function () {
1797          map.goHome();
1798          // in case drillDown is enabled, we hide everything else
1799          iMapsManager.resetDrilldown(id);
1800          // reset actions
1801          if (
1802            typeof iMapsActions !== 'undefined' &&
1803            typeof iMapsActions.resetActions !== 'undefined'
1804          ) {
1805            iMapsActions.resetActions(id);
1806          }
1807        });
1808        homeButton.icon = new am4core.Sprite();
1809        homeButton.padding(7, 5, 7, 5);
1810        homeButton.width = 30;
1811        homeButton.icon.path =
1812          'M16,8 L14,8 L14,16 L10,16 L10,10 L6,10 L6,16 L2,16 L2,8 L0,8 L8,0 L16,8 Z M16,8';
1813        homeButton.marginBottom = 3;
1814        homeButton.parent = map.zoomControl;
1815        homeButton.insertBefore(map.zoomControl.plusButton); // fix for Firefox home button not being loaded correctly
1816
1817        map.events.on('inited', function () {
1818          homeButton.deepInvalidate();
1819        });
1820      }
1821    } // outside zoom controls
1822
1823    if (im.bool(data.zoom.externalControls)) {
1824      // add home button
1825      iMapsManager.handleExternalZoom(data.id);
1826    }
1827  } // full screen button
1828
1829  if (typeof data.fullScreen !== 'undefined' && im.bool(data.fullScreen.enabled)) {
1830    // full screen
1831    var fullScreenButton = map.chartContainer.createChild(am4core.Button);
1832    fullScreenButton.events.on('hit', function (ev) {
1833      var parentMap = document.querySelector('#map_' + ev.target.icon.mapID);
1834      var mainParent = parentMap.closest('.map_wrapper');
1835
1836      // browser fullscreen
1837      iMapsManager.toggleFullscreen(mainParent);
1838      iMapsManager.isFullScreen = ev.target;
1839    });
1840
1841    // Add button
1842    fullScreenButton.align = data.fullScreen.align;
1843    fullScreenButton.valign = data.fullScreen.valign;
1844    fullScreenButton.margin(5, 5, 5, 5);
1845    fullScreenButton.padding(5, 0, 5, 0);
1846    fullScreenButton.width = 30;
1847    fullScreenButton.icon = new am4core.Sprite();
1848    fullScreenButton.icon.path = iMapsManager.library.icons.goFullIconPath;
1849    fullScreenButton.icon.isFullScreen = false;
1850    fullScreenButton.icon.mapID = id; // if mobile only, add class
1851
1852    if (im.bool(data.fullScreen.mobileOnly)) {
1853      fullScreenButton.id = '_fullscreen_button_only_mobile';
1854    } else {
1855      fullScreenButton.id = '_fullscreen_button';
1856    }
1857
1858    // Solution for Firefox issue with button container
1859    map.events.on('inited', function () {
1860      fullScreenButton.deepInvalidate();
1861    });
1862  }
1863
1864  // pan events?
1865  map.events.on('mappositionchanged', function () {
1866    // what to do here to have tooltips always display follow pan?
1867  });
1868
1869  // zoom level changed events
1870  map.events.on('zoomlevelchanged', function (ev) {
1871    var clusterSeries = im.maps[id].clusterSeries,
1872      closest,
1873      zlevel = ev.target.zoomLevel,
1874      isDrill = im.bool(data.drillDownOnClick),
1875      // Check if data.liveFilter exists and has the property 'enabled' before accessing it
1876      isDrilling = im.maps[id].isDrilling,
1877      drilledTo = im.maps[id].drilledTo,
1878      activeMap = im.filteredMap,
1879      keepBase = false;
1880
1881    var hasLiveFilter = false;
1882    if (typeof data.liveFilter !== 'undefined') {
1883      var hasLiveFilter = im.bool(data.liveFilter.enabled);
1884    }
1885
1886    // for drilldown
1887    if (isDrill && typeof data.alwaysKeepBase !== 'undefined' && im.bool(data.alwaysKeepBase)) {
1888      keepBase = true;
1889    }
1890
1891    // for live filter
1892    // Added check to ensure data.liveFilter exists before checking its 'keepBase' property
1893    if (hasLiveFilter && data.liveFilter && im.bool(data.liveFilter.keepBase)) {
1894      keepBase = true;
1895    }
1896
1897    // where the magic happens to show/hide series from cluster markers
1898    if (clusterSeries && Object.keys(clusterSeries).length) {
1899      Object.keys(clusterSeries).forEach(function (key) {
1900        // if the drilldown or live filter are enabled
1901        if (isDrill || hasLiveFilter) {
1902          if (
1903            //active map in live filter is the map the cluster belongs to
1904            (hasLiveFilter && parseInt(activeMap) === parseInt(clusterSeries[key].overlay)) ||
1905            // current active map is the base map in the filter
1906            (hasLiveFilter && parseInt(activeMap) === parseInt(id)) ||
1907            // there's a filter, but the keepBase is enabled, so clusters that are not overlay still display
1908            (hasLiveFilter && keepBase && !im.bool(clusterSeries[key].overlay)) ||
1909            //map being drilled to is the map the cluster belongs to
1910            (isDrilling &&
1911              drilledTo &&
1912              parseInt(drilledTo) === parseInt(clusterSeries[key].overlay)) ||
1913            //drilldown is enabled and cluster is from the base map and we are not drilling
1914            (isDrill && !im.bool(clusterSeries[key].overlay) && !keepBase && !drilledTo) ||
1915            //drilldown is enabled, cluster is from base map and keepBase is enabled
1916            (isDrill && !im.bool(clusterSeries[key].overlay) && keepBase)
1917          ) {
1918            // first we get the closest zoom value compared to the current zom
1919            closest = im.getClosest(clusterSeries[key].zoomLevels, zlevel);
1920            Object.keys(clusterSeries[key].zoomLevels).forEach(function (zkey) {
1921              clusterSeries[key].zoomLevels[zkey].hide();
1922              if (parseFloat(zkey) === closest) {
1923                clusterSeries[key].zoomLevels[zkey].show();
1924              } else {
1925                clusterSeries[key].zoomLevels[zkey].hide();
1926              }
1927            });
1928          } else {
1929            // not drilling and not active map
1930            Object.keys(clusterSeries[key].zoomLevels).forEach(function (zkey) {
1931              clusterSeries[key].zoomLevels[zkey].hide();
1932            });
1933          }
1934
1935          return;
1936        }
1937
1938        // it's not a drilldown, proceed
1939        // we go through the series to show the closest one and hide the others
1940        closest = im.getClosest(clusterSeries[key].zoomLevels, zlevel);
1941        Object.keys(clusterSeries[key].zoomLevels).forEach(function (zkey) {
1942          // we hide them by default
1943          clusterSeries[key].zoomLevels[zkey].hide();
1944
1945          if (parseFloat(zkey) === closest) {
1946            clusterSeries[key].zoomLevels[zkey].show();
1947          } else {
1948            clusterSeries[key].zoomLevels[zkey].hide();
1949          }
1950        });
1951      });
1952    }
1953  });
1954};
1955/**
1956 * Gets closest zoom level based on current zoom and array of zoom levels for cluster
1956s
1957 * @param {*} zoomLevels
1958 * @param {*} zlevel
1959 */
1960
1961iMapsManager.getClosest = function (zoomLevels, zlevel) {
1962  var closest = Object.keys(zoomLevels).reduce(function (prev, curr) {
1963    prev = parseInt(prev);
1964    curr = parseInt(curr);
1965    return Math.abs(curr - zlevel) < Math.abs(prev - zlevel) ? curr : prev;
1966  });
1967  return closest;
1968};
1969
1970/* Reset drilldown behaviour */
1971iMapsManager.resetDrilldown = function (id) {
1972  var im = this,
1973    data = im.maps[id].data,
1974    allCurrentSeries = im.maps[id].series,
1975    allBaseSeries = im.maps[id].allBaseSeries,
1976    bgSeries = im.maps[id].backgroundSeries;
1977
1978  if (im.bool(data.drillDownOnClick)) {
1979    for (var i = 0, len = allCurrentSeries.length; i < len; i++) {
1980      allCurrentSeries[i].hide(); //map.deltaLongitude = 0;
1981    }
1982
1983    for (var ib = 0, lenb = allBaseSeries.length; ib < lenb; ib++) {
1984      // this is messing the cluster markers on base map
1985      allBaseSeries[ib].show();
1986
1987      // make sure there's a bg series
1988      if (bgSeries.mapPolygons) {
1989        bgSeries.show();
1990      }
1991    }
1992
1993    iMapsManager.maps[id].drilledTo = false;
1994    iMapsManager.maps[id].isDrilling = false;
1995  }
1996};
1997
1998/** Manually fix lat/long offset for some countries in default projections/values */
1999
2000iMapsManager.latLongOffsetFix = function (data, mapObj, defaultZoom) {
2001  var mapSelected = data.map;
2002  var mapsFixInclude = [
2003    'russiaLow',
2004    'russiaHigh',
2005    'russiaCrimeaLow',
2006    'russiaCrimeaHigh',
2007    'region/world/asiaLow',
2008    'region/world/asiaHigh',
2009    'region/world/asiaUltra',
2010    'region/world/asiaIndiaLow',
2011    'region/world/asiaIndiaHigh',
2012    'region/world/asiaIndiaUltra',
2013  ];
2014
2015  // only do the fix if Russia is included
2016
2017  if (mapsFixInclude.includes(mapSelected) && data.exclude && !data.exclude.includes('RU')) {
2018    mapObj.deltaLongitude = -100;
2019
2020    if (defaultZoom) {
2021      mapObj.homeZoomLevel = 1.5;
2022    }
2023  }
2024
2025  if (mapSelected === 'newZealandLow' || mapSelected == 'newZealandHigh') {
2026    mapObj.deltaLongitude = 20;
2027  }
2028};
2029/**
2030 * Push region series that are grouped together
2031 */
2032
2033iMapsManager.pushGroupSeries = function (id, data) {
2034  var series = [];
2035  var regionSerie;
2036  data.regionGroups.forEach(function (group) {
2037    var newData = {},
2038      include = group.map(function (a) {
2039        return a.id;
2040      });
2041
2042    // let's get all the options from the main map and change the group option to true
2043    newData = Object.assign({}, data);
2044    newData.regionsGroupHover = true;
2045    newData.regions = group;
2046    newData.include = include; // include only the regions we're grouping
2047    regionSerie = iMapsManager.pushRegionSeries(id, newData, true);
2048    series.push(regionSerie);
2049  });
2050  return series;
2051};
2052/**
2053 * Push different series of overlay/child maps
2054 * int i - index of the map data
2055 * data - object with map data to push
2056 */
2057
2058iMapsManager.pushSeries = function (id, data) {
2059  var im = this,
2060    regionSeries,
2061    markerSeries,
2062    labelSeries,
2063    lineSeries,
2064    iconSeries,
2065    imageSeries,
2066    groupedSeries,
2067    clusterSeries = im.maps[id].clusterSeries,
2068    parentData = im.maps[id].data,
2069    seriesIndex = im.maps[id].seriesIndex,
2070    seriesById = im.maps[id].seriesById,
2071    isDrill = im.bool(im.maps[id].data.drillDownOnClick),
2072    cleanMapName = iMapsRouter.getCleanMapName(data.map, data.id),
2073    defaultSeries = false;
2074
2075  if (false === cleanMapName) {
2076    return;
2077  }
2078
2079  if (typeof data.id === 'undefined') {
2080    return;
2081  }
2082
2083  seriesById[data.id] = [];
2084
2085  // check if it's set a map overlay by default
2086  if (
2087    typeof parentData.liveFilter !== 'undefined' &&
2088    parseInt(parentData.liveFilter['default']) !== parentData.id
2089  ) {
2090    defaultSeries = parseInt(parentData.liveFilter['default']);
2091    im.filteredMap = defaultSeries;
2092  } // setup series index
2093
2094  if (!Array.isArray(im.maps[id].seriesIndex[data.map])) {
2095    im.maps[id].seriesIndex[cleanMapName] = [];
2096  }
2097
2098  // to allow empty maps to overlay, we removed the check && data.regions.length and send empty array
2099  if (!Array.isArray(data.regions)) {
2100    data.regions = [];
2101  }
2102
2103  if (typeof parentData.allowEmpty === 'undefined') {
2104    parentData.allowEmpty = 0;
2105  } // in case we want empty maps to overlay, we remove this check.
2106  // in are not allowing the empty so that the live filter works better and markers are not hidden behind the map
2107  // reference: https://interactivegeomaps.com/feature/live-filter/
2108  // but other overlays and empty maps might need to be added...
2109
2110  // we don't check if the regions.length exist, because maybe user wants to add an empty map, only to show divisions
2111  if (data.regions.length || im.bool(parentData.allowEmpty)) {
2112    // in case we don't allow empty, we only include the active regions
2113    if (!im.bool(parentData.allowEmpty)) {
2114      data.include = [];
2115      data.regions.forEach(function (region) {
2116        data.include.push(region.id);
2117        if (!isNaN(region.id)) {
2118          data.include.push(parseInt(region.id));
2119        }
2120      });
2121    }
2122
2123    regionSeries = iMapsManager.pushRegionSeries(id, data);
2124    seriesIndex[cleanMapName].push(regionSeries);
2125    seriesById[data.id].push(regionSeries);
2126
2127    if (isDrill) {
2128      regionSeries.hidden = true;
2129      regionSeries.mapID = data.id;
2130    }
2131
2132    // hide in case we have a live filter and this is not the default
2133    if (
2134      parentData.liveFilter &&
2135      im.bool(parentData.liveFilter.enabled) &&
2136      defaultSeries &&
2137      defaultSeries !== data.id
2138    ) {
2139      regionSeries.hidden = true;
2140    }
2141  }
2142
2143  // Check for grouped regions
2144  if (Array.isArray(data.regionGroups) && data.regionGroups.length) {
2145    groupedSeries = im.pushGroupSeries(id, data);
2146    groupedSeries.forEach(function (regionSerie) {
2147      seriesIndex[cleanMapName].push(regionSerie);
2148      seriesById[data.id].push(regionSerie);
2149
2150      if (isDrill) {
2151        regionSerie.hidden = true;
2152        regionSerie.mapID = data.id;
2153      }
2154
2155      // hide in case we have a live filter and this is not the default
2156      if (
2157        parentData.liveFilter &&
2158        im.bool(parentData.liveFilter.enabled) &&
2159        defaultSeries &&
2160        defaultSeries !== data.id
2161      ) {
2162        regionSerie.hidden = true;
2163      }
2164    });
2165  } // let's add lines before markers, so that the markers then overlay the end of lines, in case
2166
2167  if (Array.isArray(data.lines) && data.lines.length) {
2168    lineSeries = iMapsManager.pushLineSeries(id, data);
2169    seriesIndex[cleanMapName].push(lineSeries);
2170    seriesById[data.id].push(lineSeries);
2171
2172    if (isDrill) {
2173      lineSeries.hidden = true;
2174    } // hide in case we have a live filter and this is not the default
2175
2176    if (
2177      parentData.liveFilter &&
2178      im.bool(parentData.liveFilter.enabled) &&
2179      defaultSeries &&
2180      defaultSeries !== data.id
2181    ) {
2182      lineSeries.hidden = true;
2183    }
2184  }
2185
2186  if (Array.isArray(data.roundMarkers) && data.roundMarkers.length) {
2187    markerSeries = iMapsManager.pushRoundMarkerSeries(id, data);
2188    seriesIndex[cleanMapName].push(markerSeries);
2189    seriesById[data.id].push(markerSeries);
2190
2191    if (isDrill) {
2192      markerSeries.hidden = true;
2193      markerSeries.mapID = data.id;
2194    }
2195
2196    // clusters in overlay maps
2197    if (data.clusterMarkers && im.bool(data.clusterMarkers.enabled)) {
2198      markerSeries.hidden = true;
2199      im.setupClusters(data, id, data.id);
2200      clusterSeries[data.id].zoomLevels[data.clusterMarkers.zoomLevel] = markerSeries; // we setup the main index series (zoom=1) to be visible
2201      // when doing it inside setupClusters function, there was a bug
2202
2203      if (!isDrill && clusterSeries[data.id].zoomLevels[1]) {
2204        clusterSeries[data.id].zoomLevels[1].hidden = false;
2205
2206        // if there's a live filter, double check - we hide them.
2207        // otherwise overlaid series with clusters were always displaying, even when they shouldn't
2208        if (
2209          parentData.liveFilter &&
2210          im.bool(parentData.liveFilter.enabled) &&
2211          defaultSeries &&
2212          defaultSeries !== data.id
2213        ) {
2214          clusterSeries[data.id].zoomLevels[1].hidden = true;
2215        }
2216      }
2217    }
2218
2219    // hide in case we have a live filter and this is not the default
2220    if (
2221      parentData.liveFilter &&
2222      im.bool(parentData.liveFilter.enabled) &&
2223      defaultSeries &&
2224      defaultSeries !== data.id
2225    ) {
2226      markerSeries.hidden = true;
2227    }
2228  }
2229
2230  if (Array.isArray(data.iconMarkers) && data.iconMarkers.length) {
2231    iconSeries = iMapsManager.pushIconMarkerSeries(id, data);
2232    seriesIndex[cleanMapName].push(iconSeries);
2233    seriesById[data.id].push(iconSeries);
2234
2235    if (isDrill) {
2236      iconSeries.hidden = true;
2237    } // hide in case we have a live filter and this is not the default
2238
2239    if (
2240      parentData.liveFilter &&
2241      im.bool(parentData.liveFilter.enabled) &&
2242      defaultSeries &&
2243      defaultSeries !== data.id
2244    ) {
2245      iconSeries.hidden = true;
2246    }
2247  }
2248
2249  if (Array.isArray(data.imageMarkers) && data.imageMarkers.length) {
2250    imageSeries = iMapsManager.pushImageMarkerSeries(id, data);
2251    seriesIndex[cleanMapName].push(imageSeries);
2252    seriesById[data.id].push(imageSeries);
2253
2254    if (isDrill) {
2255      imageSeries.hidden = true;
2256    } // hide in case we have a live filter and this is not the default
2257
2258    if (
2259      parentData.liveFilter &&
2260      im.bool(parentData.liveFilter.enabled) &&
2261      defaultSeries &&
2262      defaultSeries !== data.id
2263    ) {
2264      imageSeries.hidden = true;
2265    }
2266  }
2267
2268  if (Array.isArray(data.labels) && data.labels.length) {
2269    labelSeries = iMapsManager.pushLabelSeries(id, data);
2270    seriesIndex[cleanMapName].push(labelSeries);
2271    seriesById[data.id].push(labelSeries);
2272
2273    if (isDrill) {
2274      labelSeries.hidden = true;
2275    } // hide in case we have a live filter and this is not the default
2276
2277    if (
2278      parentData.liveFilter &&
2279      im.bool(parentData.liveFilter.enabled) &&
2280      defaultSeries &&
2281      defaultSeries !== data.id
2282    ) {
2283      labelSeries.hidden = true;
2284    }
2285  }
2286};
2287
2288iMapsManager.pushRegionSeries = function (id, data, groupHover) {
2289  var im = this,
2290    map = im.maps[id].map,
2291    // shorter reference for the map
2292    regionSeries,
2293    tooltipConfig = data.tooltip || {},
2294    regionTemplate,
2295    hover,
2296    active,
2297    highlight,
2298    mapVar,
2299    clkLabel,
2300    groupHover = groupHover || false;
2301  data = data || {};
2302  regionSeries = map.series.push(new am4maps.MapPolygonSeries());
2303
2304  if (data.map === 'custom' || im.bool(data.useGeojson)) {
2305    regionSeries.geodataSource.url = data.mapURL;
2306  } else {
2307    mapVar = iMapsRouter.getVarByName(data.map);
2308    regionSeries.geodata = window[mapVar];
2309  }
2310
2311  regionSeries.name =
2312    data.regionLegend && data.regionLegend.title !== '' ? data.regionLegend.title : data.title;
2313  regionSeries.hiddenInLegend = data.regionLegend ? !im.bool(data.regionLegend.enabled) : true; // if it's a base series
2314
2315  // override hide in legend for scenarios where there are grouped entries for example
2316  if (data.regionLegend && data.regionLegend.enabled === 'onlyGroups' && groupHover) {
2317    regionSeries.hiddenInLegend = false;
2318  }
2319
2320  if (data.regionLegend && data.regionLegend.enabled === 'onlyGroups' && !groupHover) {
2321    regionSeries.hiddenInLegend = true;
2322  }
2323
2324  if (data.regionLegend && data.regionLegend.enabled === 'ignoreGroups' && groupHover) {
2325    regionSeries.hiddenInLegend = true;
2326  }
2327
2328  if (data.regionLegend && data.regionLegend.enabled === 'ignoreGroups' && !groupHover) {
2329    regionSeries.hiddenInLegend = false;
2330  }
2331
2332  if (id === data.id) {
2333    // add it as the baseSeries - which will contain all region base series
2334    if (typeof im.maps[id].baseSeries === 'undefined') {
2335      im.maps[id].baseSeries = [];
2336    }
2337    im.maps[id].baseSeries.push(regionSeries);
2338    im.maps[id].allBaseSeries.push(regionSeries);
2339  }
2340
2341  // Make map load polygon (like country names) data from GeoJSON
2342  regionSeries.useGeodata = true; // Exclude
2343
2344  if (Array.isArray(data.exclude) && data.exclude.length) {
2345    regionSeries.exclude = data.exclude;
2346  } // Include
2347
2348  if (Array.isArray(data.include) && data.include.length) {
2349    regionSeries.include = data.include;
2350  } // Setup Heatmap rules
2351
2352  if (data.heatMapRegions && im.bool(data.heatMapRegions.enabled)) {
2353    im.setupHeatMap(regionSeries, id, data);
2354  } // Data
2355  // if (Array.isArray(data.regions)) {
2356
2357  regionSeries.data = data.regions; // Configure series
2358
2359  if (groupHover) {
2360    regionSeries.groupHover = true;
2361  }
2362
2363  regionTemplate = regionSeries.mapPolygons.template;
2364  im.setupTooltip(id, regionSeries, data); // check for custom tooltip template
2365
2366  if (
2367    typeof data.regionsTooltipTemplate !== 'undefined' &&
2368    data.regionsTooltipTemplate.trim() !== ''
2369  ) {
2370    regionTemplate.tooltipText = data.regionsTooltipTemplate;
2371    regionTemplate.tooltipHTML = data.regionsTooltipTemplate;
2372  } else {
2373    regionTemplate.tooltipText = tooltipConfig.template
2374      ? tooltipConfig.template
2375      : '{tooltipContent}';
2376    regionTemplate.tooltipHTML = tooltipConfig.template
2377      ? tooltipConfig.template
2378      : '{tooltipContent}';
2379  }
2380
2381  regionTemplate.adapter.add('tooltipHTML', function (value, target) {
2382    if (
2383      _typeof(target.dataItem.dataContext) === 'object' &&
2384      typeof tooltipConfig.onlyWithData !== 'undefined'
2385    ) {
2386      // check if we don't return tooltip on empty regions
2387      if (im.bool(tooltipConfig.onlyWithData)) {
2388        if (target.dataItem.dataContext.madeFromGeoData === true) {
2389          target.tooltip.tooltipText = undefined;
2390          target.tooltip.tooltipHTML = undefined;
2391          return '';
2392        }
2393      }
2394    }
2395
2396    if (value === '') {
2397      return value;
2398    }
2399
2400    return value.replace(/\\/g, '');
2401  });
2402  regionTemplate.adapter.add('tooltipText', function (value, target) {
2403    if (
2404      _typeof(target.dataItem.dataContext) === 'object' &&
2405      typeof tooltipConfig.onlyWithData !== 'undefined'
2406    ) {
2407      // check if we don't return tooltip on empty regions
2408      if (im.bool(tooltipConfig.onlyWithData)) {
2409        if (target.dataItem.dataContext.madeFromGeoData === true) {
2410          target.tooltip.tooltipText = undefined;
2411          target.tooltip.tooltipHTML = undefined;
2412          return '';
2413        }
2414      }
2415    }
2416
2417    if (value === '') {
2418      return value;
2419    }
2420
2421    return value.replace(/\\/g, '');
2422  }); // For legend color
2423
2424  regionSeries.fill = data.regionDefaults.fill;
2425  regionTemplate.fill = data.regionDefaults.inactiveColor;
2426  regionTemplate.stroke = data.visual.borderColor;
2427  regionTemplate.strokeWidth = data.visual.borderWidth; // fill
2428
2429  regionTemplate.propertyFields.fill = 'fill';
2430
2431  // exploring adapter
2432  /*
2433		regionTemplate.adapter.add("fill", function(fill, target) {
2434			if(Array.isArray(fill)){
2435	      let gradient = new am4core.LinearGradient();
2436	      fill.forEach(function(color){
2437	        gradient.addColor(am4core.color(color));
2438	      });
2439	      fill = gradient;
2440	    }
2441	    return fill;
2442		});
2443	  */
2444
2445  // hover - only create if it's not a group hover series
2446
2447  if (!groupHover) {
2448    // also don't consider it on heatmaps with hover disabled
2449    if (
2450      typeof data.heatMapRegions !== 'undefined' &&
2451      im.bool(data.heatMapRegions.enabled) &&
2452      im.bool(data.heatMapRegions.noHover)
2453    ) {
2454      // do nothing for now, we don't need hover, might implement something at some point
2455    } else {
2456      hover = regionTemplate.states.create('hover');
2457      hover.propertyFields.fill = 'hover';
2458    }
2459    //hover.propertyFields.strokeWidth = "borderWidthHover";
2460    //hover.propertyFields.stroke = "borderColorHover";
2461  }
2462
2463  // active state
2464  if (data.regionActiveState && im.bool(data.regionActiveState.enabled)) {
2465    active = regionTemplate.states.create('active');
2466
2467    if (data.regionActiveState.source === 'custom') {
2468      active.properties.fill = data.regionActiveState.fill;
2469    } else {
2470      active.propertyFields.fill = 'hover';
2471    }
2472  }
2473  // highlight - for group hover
2474  highlight = regionTemplate.states.create('highlight');
2475  highlight.propertyFields.fill = 'hover';
2476
2477  if (groupHover) {
2478    regionTemplate.events.on('out', function (ev) {
2479      im.groupHoverOut(id, ev);
2480    });
2481    regionTemplate.events.on('over', function (ev) {
2482      im.groupHover(id, ev);
2483      // bring this series (and label series if it exists) to front when group is hovered
2484      regionSeries.toFront();
2485      if (typeof regionSeries.autoLabelSeries !== 'undefined') {
2486        regionSeries.autoLabelSeries.toFront();
2487      }
2488    });
2489    regionTemplate.events.on('hit', function (ev) {
2490      im.groupHit(id, ev);
2491    });
2492  } else {
2493    regionTemplate.events.on('hit', function (ev) {
2494      im.singleHit(id, ev);
2495    });
2496    regionTemplate.events.on('over', function (ev) {
2497      im.setupHoverEvents(id, ev);
2498    });
2499  } // Events
2500
2501  // iOS bug fix, remove focus
2502  regionTemplate.focusable = false;
2503
2504  regionTemplate.events.on('hit', function (ev) {
2505    im.setupHitEvents(id, ev);
2506  });
2507
2508  // enable small map
2509  if (im.bool(data.smallMap)) {
2510    map.smallMap.series.push(regionSeries);
2511  }
2512
2513  // auto Labels
2514  if (data.regionLabels && im.bool(data.regionLabels.source)) {
2515    regionSeries.calculateVisualCenter = true;
2516
2517    // Configure label series
2518    var labelSeries = map.series.push(new am4maps.MapImageSeries());
2519    var labelTemplate = labelSeries.mapImages.template.createChild(am4core.Label);
2520
2521    // Label Background - currently only possible with global
2522    var background = typeof igmLabelsBackground !== 'undefined' ? igmLabelsBackground : false;
2523    if (typeof background === 'object') {
2524      labelTemplate.background = new am4core.RoundedRectangle();
2525      labelTemplate.background.cornerRadius(...background.cornerRadius);
2526      labelTemplate.background.fill = am4core.color(background.color);
2527      labelTemplate.padding(...background.padding);
2528      labelTemplate.background.stroke = am4core.color(background.stroke);
2529    }
2530
2531    im.maps[id].labelSeries.push(labelSeries);
2532
2533    // add reference to parent region series
2534    regionSeries.autoLabelSeries = labelSeries;
2535
2536    labelTemplate.horizontalCenter = data.regionLabels.horizontalCenter;
2537    labelTemplate.verticalCenter = data.regionLabels.verticalCenter;
2538    labelTemplate.fontSize = data.regionLabels.fontSize;
2539    labelTemplate.fill = data.regionLabels.fill;
2540    // automatic region label hover state
2541    const hoverState = labelTemplate.states.create('hover');
2542    hoverState.properties.fill = am4core.color(data.regionLabels.hover);
2543
2544    labelTemplate.fontFamily = data.regionLabels.fontFamily
2545      ? data.regionLabels.fontFamily
2546      : 'inherit';
2547    labelTemplate.fontWeight = data.regionLabels.fontWeight
2548      ? data.regionLabels.fontWeight
2549      : 'normal';
2550
2551    // let's set a listener to the hide event of main series to hide this one also
2552    if (data.regionLabels.mobileFontSize && parseInt(data.regionLabels.mobileFontSize) !== 100) {
2553      if (window.innerWidth <= 780) {
2554        labelTemplate.fontSize =
2555          (parseInt(labelTemplate.fontSize) * parseInt(data.regionLabels.mobileFontSize)) / 100;
2556        labelTemplate.size = labelTemplate.fontSize;
2557      }
2558    }
2559
2560    regionSeries.events.on('hidden', function () {
2561      labelSeries.hide();
2562    });
2563    regionSeries.events.on('shown', function () {
2564      labelSeries.show();
2565    });
2566    // label events
2567    labelTemplate.events.on('hit', function (ev) {
2568      clkLabel = regionSeries.getPolygonById(ev.target.parent.LabelForRegion);
2569      clkLabel.dispatchImmediately('hit');
2570    });
2571    labelTemplate.events.on('over', function (ev) {
2572      iMapsManager.hover(id, ev.target.parent.LabelForRegion, false);
2573    });
2574    labelTemplate.events.on('out', function (ev) {
2575      iMapsManager.clearHovered(id, ev.target.parent.LabelForRegion);
2576    });
2577
2578    im.setupTooltip(id, labelSeries, data, labelTemplate);
2579    labelTemplate.interactionsEnabled = true;
2580    labelTemplate.nonScaling = im.bool(data.regionLabels.nonScaling);
2581    labelSeries.hiddenInLegend = true; // fix initially hidden series - for example drilldown overlay
2582
2583    regionSeries.events.on('inited', function () {
2584      if (regionSeries.hidden) {
2585        labelSeries.hide();
2586        labelSeries.hidden = true;
2587      }
2588    });
2589
2590    // set labels drag listener
2591    // allow labels to be dragged if in admin
2592
2593    if (im.bool(data.admin)) {
2594      labelTemplate.draggable = true;
2595      labelTemplate.cursorOverStyle = am4core.MouseCursorStyle.grab;
2596      labelTemplate.events.on('dragstop', function (ev) {
2597        var svgPoint = am4core.utils.spritePointToSvg(
2598          {
2599            x: 0,
2600            y: 0,
2601          },
2602          ev.target
2603        );
2604        svgPoint = am4core.utils.spritePointToSvg(
2605          {
2606            x: 0 - ev.target.maxLeft,
2607            y: 0 - ev.target.maxTop,
2608          },
2609          ev.target
2610        );
2611        var geo = map.svgPointToGeo(svgPoint); // check if field to add json object with adjustments exists
2612
2613        var adjField = document.querySelector(
2614          '[data-depend-id=' + data.regionLabelCustomCoordinates + ']'
2615        );
2616
2617        if (adjField) {
2618          var jsonAdjustments;
2619
2620          if (iMapsManager.isJSON(adjField.value)) {
2621            jsonAdjustments = JSON.parse(adjField.value);
2622          } else {
2623            jsonAdjustments = {};
2624          }
2625
2626          jsonAdjustments[ev.target.parent.LabelForRegion] = {
2627            latitude: geo.latitude,
2628            longitude: geo.longitude,
2629          };
2630          adjField.value = JSON.stringify(jsonAdjustments);
2631        }
2632
2633        map.seriesContainer.draggable = im.bool(data.zoom.draggable);
2634        ev.target.cursorOverStyle = am4core.MouseCursorStyle.grab;
2635      });
2636      labelTemplate.events.on('down', function (ev) {
2637        map.seriesContainer.draggable = false;
2638        ev.target.cursorOverStyle = am4core.MouseCursorStyle.grabbing;
2639      });
2640    } // end drag event
2641
2642    // convert custom json position string to object
2643    var regionLabelCustomCoordinates = im.isJSON(data.regionLabels.regionLabelCustomCoordinates)
2644      ? JSON.parse(data.regionLabels.regionLabelCustomCoordinates)
2645      : false;
2646
2647    let event = 'inited';
2648    if (data.map === 'custom') {
2649      event = 'datavalidated';
2650    }
2651
2652    regionSeries.events.on(event, function () {
2653      regionSeries.mapPolygons.each(function (polygon) {
2654        // if they are not displaying with lat/long, skip, unless we're using a custom map
2655        if (data.map !== 'custom' && typeof polygon.visualLatitude === 'undefined') {
2656          return;
2657        }
2658
2659        var label = labelSeries.mapImages.create(),
2660          text; // if we're only adding labels to active regions
2661
2662        if (
2663          im.bool(data.regionLabels.activeOnly) &&
2664          polygon.dataItem.dataContext &&
2665          typeof polygon.dataItem.dataContext.tooltipContent === 'undefined'
2666        ) {
2667          return;
2668        } // if it's a group entry, ignore
2669
2670        if (polygon.dataItem.dataContext.id.includes(',')) {
2671          return;
2672        }
2673
2674        if (data.regionLabels.source === 'code') {
2675          text = polygon.dataItem.dataContext.id.split('-').pop();
2676        }
2677
2678        if (data.regionLabels.source === '{name}') {
2679          text = polygon.dataItem.dataContext.name;
2680        }
2681
2682        if (data.regionLabels.source === '{id}') {
2683          text = polygon.dataItem.dataContext.id;
2684        }
2685
2686        if (
2687          data.regionLabels.source === 'custom' &&
2688          typeof data.regionLabels.customSource !== 'undefined'
2689        ) {
2690          text = polygon.dataItem.dataContext.autoLabel;
2691        }
2692
2693        label.LabelForRegion = polygon.dataItem.dataContext.id; // if it was a group
2694
2695        if (typeof polygon.dataItem.dataContext.originalID !== 'undefined') {
2696          label.groupRegion = polygon.dataItem.dataContext.originalID;
2697        }
2698
2699        // tooltip content
2700        label.tooltipDataItem = polygon.tooltipDataItem;
2701        label.tooltip = polygon.tooltip;
2702        label.tooltipHTML = polygon.tooltipHTML;
2703        label.tooltipPosition = im.bool(data.tooltip.fixed) ? 'fixed' : 'pointer'; // cursor style
2704
2705        if (polygon.dataItem.dataContext.action && polygon.dataItem.dataContext.action !== 'none') {
2706          label.cursorOverStyle = am4core.MouseCursorStyle.pointer;
2707        }
2708
2709        // use custom coordinates adjustments or use auto position
2710        if (
2711          regionLabelCustomCoordinates &&
2712          regionLabelCustomCoordinates.hasOwnProperty(polygon.dataItem.dataContext.id)
2713        ) {
2714          label.latitude = regionLabelCustomCoordinates[polygon.dataItem.dataContext.id].latitude;
2715          label.longitude = regionLabelCustomCoordinates[polygon.dataItem.dataContext.id].longitude;
2716        } else {
2717          if (polygon.visualLatitude) {
2718            label.latitude = polygon.visualLatitude;
2719            label.longitude = polygon.visualLongitude;
2720          }
2721        }
2722
2723        if (label.children.getIndex(0)) {
2724          label.children.getIndex(0).text = text;
2725        }
2726      });
2727    });
2728  }
2729
2730  // if the external dropdown is enabled, calculate visual center
2731
2732  if (typeof data.externalDropdown !== 'undefined' && im.bool(data.externalDropdown.enabled)) {
2733    regionSeries.calculateVisualCenter = true;
2734  } // add this series to map series to reference it later if needed
2735
2736  im.maps[id].series.push(regionSeries);
2737  return regionSeries;
2738};
2739
2740iMapsManager.pushRoundMarkerSeries = function (id, data) {
2741  var im = this,
2742    map = im.maps[id].map,
2743    // shorter reference for the map
2744    markerSeries,
2745    markerSeriesTemplate,
2746    circle,
2747    label,
2748    activeState,
2749    highlightState,
2750    hoverState;
2751
2752  if (Array.isArray(data.roundMarkers) && data.roundMarkers.length) {
2753    // Create image series
2754    markerSeries = map.series.push(new am4maps.MapImageSeries());
2755    markerSeries.name =
2756      data.roundMarkersLegend && data.roundMarkersLegend.title !== ''
2757        ? data.roundMarkersLegend.title
2758        : data.title;
2759    markerSeries.hiddenInLegend = data.roundMarkersLegend
2760      ? !im.bool(data.roundMarkersLegend.enabled)
2761      : false; // Create a circle image in image series template so it gets replicated to all new images
2762
2763    markerSeriesTemplate = markerSeries.mapImages.template;
2764
2765    circle = markerSeriesTemplate.createChild(am4core.Circle);
2766    im.setupTooltip(id, markerSeries, data, circle);
2767
2768    //iOS focus scroll bug fix
2769    markerSeriesTemplate.focusable = false;
2770
2771    circle.radius = data.markerDefaults.radius;
2772    circle.fill = data.markerDefaults.fill;
2773    circle.stroke = am4core.color('#FFFFFF');
2774    circle.strokeWidth = 1;
2775    circle.nonScaling =
2776      typeof igmRoundMarkersNonScaling !== 'undefined' ? igmRoundMarkersNonScaling : true;
2777
2778    // label
2779    label = markerSeriesTemplate.createChild(am4core.Label);
2780    label.text = '{label}';
2781    label.fill = am4core.color('#fff');
2782    label.verticalCenter = 'middle';
2783    label.horizontalCenter = 'middle';
2784    label.nonScaling =
2785      typeof igmRoundMarkersNonScaling !== 'undefined' ? igmRoundMarkersNonScaling : true;
2786    label.fontSize =
2787      typeof igmClusterMarkerFontSize !== 'undefined'
2788        ? igmClusterMarkerFontSize
2789        : data.markerDefaults.radius;
2790    label.clickable = false;
2791    label.focusable = false;
2792    label.hoverable = false;
2793
2794    // check for custom tooltip template
2795    if (
2796      typeof data.roundMarkersTooltipTemplate !== 'undefined' &&
2797      data.roundMarkersTooltipTemplate.trim() !== ''
2798    ) {
2799      markerSeriesTemplate.tooltipText = data.roundMarkersTooltipTemplate;
2800      markerSeriesTemplate.tooltipHTML = data.roundMarkersTooltipTemplate;
2801    } else {
2802      markerSeriesTemplate.tooltipText =
2803        data.tooltip && data.tooltip.template ? data.tooltip.template : '{tooltipContent}';
2804      markerSeriesTemplate.tooltipHTML =
2805        data.tooltip && data.tooltip.template ? data.tooltip.template : '{tooltipContent}';
2806    }
2807
2808    markerSeriesTemplate.propertyFields.tooltipText = 'tooltipTemplate';
2809    markerSeriesTemplate.propertyFields.tooltipHTML = 'tooltipTemplate'; // fill can only be set if heatmap is not enabled and not a range
2810
2811    if (data.heatMapMarkers && im.bool(data.heatMapMarkers.enabled)) {
2812      im.setupHeatMap(markerSeries, id, data); // setup the fill and radius if type is range
2813
2814      if (typeof data.heatMapMarkers.type !== 'undefined' && data.heatMapMarkers.type === 'range') {
2815        circle.propertyFields.radius = 'radius';
2816        circle.propertyFields.fill = 'fill';
2817      }
2818    } else {
2819      circle.propertyFields.radius = 'radius';
2820      circle.propertyFields.fill = 'fill';
2821    }
2822
2823    circle.propertyFields.userClassName = 'customClass'; // Set property fields
2824
2825    markerSeriesTemplate.propertyFields.radius = 'radius';
2826    markerSeriesTemplate.propertyFields.latitude = 'latitude';
2827    markerSeriesTemplate.propertyFields.longitude = 'longitude';
2828    markerSeriesTemplate.setStateOnChildren = true; // hover & active
2829
2830    hoverState = circle.states.create('hover');
2831    hoverState.properties.fill = data.hover;
2832    hoverState.propertyFields.fill = 'hover'; // active
2833
2834    activeState = circle.states.create('active');
2835    activeState.properties.fill = data.hover;
2836    activeState.propertyFields.fill = 'hover'; // highlight - for legend hover
2837
2838    highlightState = circle.states.create('highlight');
2839    highlightState.properties.fill = data.hover;
2840    highlightState.propertyFields.fill = 'hover'; // text label below
2841
2842    if (data.roundMarkerLabels && im.bool(data.roundMarkerLabels.enabled)) {
2843      // create dummy state to force all object to get this state
2844      markerSeriesTemplate.states.create('hover');
2845
2846      var markerLabel = markerSeriesTemplate.createChild(am4core.Label);
2847      var markerLabelPosition =
2848        typeof data.roundMarkerLabels.position !== 'undefined' &&
2849        data.roundMarkerLabels.position !== ''
2850          ? data.roundMarkerLabels.position
2851          : 'bottom';
2852      markerLabel.text =
2853        typeof data.roundMarkerLabels.source !== 'undefined' && data.roundMarkerLabels.source !== ''
2854          ? data.roundMarkerLabels.source
2855          : '{name}';
2856      markerLabel.fontSize = data.roundMarkerLabels.fontSize;
2857
2858      // set state on parent
2859      markerLabel.setStateOnChildren = true;
2860
2861      //for mobile devices
2862      if (
2863        data.roundMarkerLabels.mobileSize &&
2864        parseInt(data.roundMarkerLabels.mobileSize) !== 100
2865      ) {
2866        if (window.innerWidth <= 780) {
2867          markerLabel.fontSize =
2868            (parseInt(data.roundMarkerLabels.fontSize) *
2869              parseInt(data.roundMarkerLabels.mobileSize)) /
2870            100;
2871        }
2872      }
2873
2874      markerLabel.nonScaling =
2875        typeof igmRoundMarkersNonScaling !== 'undefined' ? igmRoundMarkersNonScaling : true;
2876      //im.bool(data.roundMarkerLabels.nonScaling);
2877
2878      markerLabel.fill = data.roundMarkerLabels.fill;
2879      markerLabel.clickable = false;
2880      markerLabel.focusable = false;
2881      markerLabel.hoverable = false;
2882
2883      // control position
2884      markerLabel.padding(0, 0, 0, 0);
2885
2886      // this is a first iteration of this feature
2887      // in the future we can have the position individually for each marker and use the propertyFields approach
2888      if (markerLabelPosition === 'bottom') {
2889        markerLabel.horizontalCenter = 'middle';
2890        markerLabel.verticalCenter = 'top';
2891        markerLabel.propertyFields.paddingTop = 'radius';
2892      }
2893
2894      if (markerLabelPosition === 'top') {
2895        markerLabel.horizontalCenter = 'middle';
2896        markerLabel.verticalCenter = 'bottom';
2897        markerLabel.propertyFields.paddingBottom = 'radius';
2898      }
2899
2900      if (markerLabelPosition === 'right') {
2901        markerLabel.horizontalCenter = 'left';
2902        markerLabel.verticalCenter = 'middle';
2903        markerLabel.propertyFields.paddingLeft = 'radius';
2904      }
2905
2906      if (markerLabelPosition === 'left') {
2907        markerLabel.horizontalCenter = 'right';
2908        markerLabel.verticalCenter = 'middle';
2909        markerLabel.propertyFields.paddingRight = 'radius';
2910      }
2911    }
2912
2913    // send to front (so on overlay they are always on top of regions)
2914    markerSeries.zIndex = Number.MAX_VALUE;
2915    markerSeries.toFront();
2916
2917    // Add data
2918    markerSeries.data = data.roundMarkers; // For legend color
2919
2920    markerSeries.fill = data.markerDefaults.fill; // Events
2921
2922    markerSeriesTemplate.events.on('hit', function (ev) {
2923      im.singleHit(id, ev);
2924      im.setupHitEvents(id, ev);
2925    });
2926    markerSeriesTemplate.events.on('over', function (ev) {
2927      im.setupHoverEvents(id, ev);
2928    });
2929  } // enable small map
2930
2931  if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) {
2932    map.smallMap.series.push(markerSeries);
2933  } // add this series to map series to reference it later if needed
2934
2935  im.maps[id].series.push(markerSeries); // if part of the parent map
2936
2937  if (id === data.id) {
2938    // only add as base if not a cluster
2939    if (data.clusterMarkers && !im.bool(data.clusterMarkers.enabled)) {
2940      im.maps[id].allBaseSeries.push(markerSeries);
2941    } else if (data.clusterMarkers && im.bool(data.clusterMarkers.enabled)) {
2942      // it's a cluster, so the main series with all markers
2943      // markerSeries.hidden = true;
2944    }
2945  }
2946
2947  return markerSeries;
2948};
2949
2950iMapsManager.pushImageMarkerSeries = function (id, data) {
2951  var im = this,
2952    map = im.maps[id].map,
2953    // shorter reference for the map
2954    markerSeries,
2955    markerSeriesTemplate,
2956    imageMarker;
2957
2958  if (Array.isArray(data.imageMarkers) && data.imageMarkers.length) {
2959    // Create image series
2960    markerSeries = map.series.push(new am4maps.MapImageSeries());
2961    markerSeries.name =
2962      data.imageMarkersLegend && data.imageMarkersLegend.title !== ''
2963        ? data.imageMarkersLegend.title
2964        : data.title;
2965    markerSeries.hiddenInLegend = data.imageMarkersLegend
2966      ? !im.bool(data.imageMarkersLegend.enabled)
2967      : false; // Create a circle image in image series template so it gets replicated to all new images
2968
2969    markerSeriesTemplate = markerSeries.mapImages.template;
2970
2971    //iOS focus scroll bug fix
2972    markerSeriesTemplate.focusable = false;
2973
2974    imageMarker = markerSeriesTemplate.createChild(am4core.Image);
2975    imageMarker.propertyFields.href = 'href';
2976    imageMarker.propertyFields.width = 'size';
2977    imageMarker.propertyFields.height = 'size';
2978    imageMarker.propertyFields.userClassName = 'customClass'; //imageMarker.propertyFields.height = "height";
2979
2980    imageMarker.propertyFields.horizontalCenter = 'horizontalCenter';
2981    imageMarker.propertyFields.verticalCenter = 'verticalCenter';
2982    imageMarker.nonScaling = true;
2983    imageMarker.propertyFields.nonScaling = 'nonScaling';
2984    im.setupTooltip(id, markerSeries, data, imageMarker); // to have tooltip display above the image
2985    //imageMarker.tooltipX = am4core.percent(50);
2986    //imageMarker.tooltipY = am4core.percent(0);
2987    // check for custom tooltip template
2988
2989    if (
2990      typeof data.imageMarkersTooltipTemplate !== 'undefined' &&
2991      data.imageMarkersTooltipTemplate.trim() !== ''
2992    ) {
2993      imageMarker.tooltipText = data.imageMarkersTooltipTemplate;
2994      imageMarker.tooltipHTML = data.imageMarkersTooltipTemplate;
2995    } else {
2996      imageMarker.tooltipText = data.tooltip.template ? data.tooltip.template : '{tooltipContent}';
2997      imageMarker.tooltipHTML = data.tooltip.template ? data.tooltip.template : '{tooltipContent}';
2998    } // Set property fields
2999
3000    markerSeriesTemplate.propertyFields.latitude = 'latitude';
3001    markerSeriesTemplate.propertyFields.longitude = 'longitude'; // Add data for the three cities
3002
3003    // send to front (so on overlay they are always on top of regions)
3004    markerSeries.zIndex = Number.MAX_VALUE;
3005    markerSeries.toFront();
3006
3007    markerSeries.data = data.imageMarkers; // Events
3008
3009    markerSeriesTemplate.events.on('hit', function (ev) {
3010      im.singleHit(id, ev);
3011      im.setupHitEvents(id, ev);
3012    });
3013    markerSeriesTemplate.events.on('over', function (ev) {
3014      im.setupHoverEvents(id, ev);
3015    });
3016  } // enable small map
3017
3018  if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) {
3019    map.smallMap.series.push(markerSeries);
3020  }
3020 // add this series to map series to reference it later if needed
3021
3022  im.maps[id].series.push(markerSeries);
3023  return markerSeries;
3024};
3025
3026iMapsManager.pushIconMarkerSeries = function (id, data) {
3027  var im = this,
3028    map = im.maps[id].map,
3029    // shorter reference for the map
3030    markerSeries,
3031    markerSeriesTemplate,
3032    icon,
3033    hover,
3034    active,
3035    highlightState,
3036    clickableOverlay;
3037
3038  if (Array.isArray(data.iconMarkers) && data.iconMarkers.length) {
3039    // Create image series
3040    markerSeries = map.series.push(new am4maps.MapImageSeries());
3041    markerSeries.hiddenInLegend = data.iconMarkersLegend
3042      ? !im.bool(data.iconMarkersLegend.enabled)
3043      : false;
3044    markerSeries.name =
3045      data.iconMarkersLegend && data.iconMarkersLegend.title !== ''
3046        ? data.iconMarkersLegend.title
3047        : data.title;
3048    markerSeriesTemplate = markerSeries.mapImages.template;
3049    markerSeriesTemplate.nonScaling = true;
3050    markerSeriesTemplate.setStateOnChildren = true; //apply parent's current state to children
3051
3052    //iOS focus scroll bug fix
3053    markerSeriesTemplate.focusable = false;
3054
3055    markerSeriesTemplate.states.create('hover'); //create dummy state for hover
3056    // Create a circle image in image series template so it gets replicated to all new images
3057
3058    icon = markerSeriesTemplate.createChild(am4core.Sprite);
3059    icon.propertyFields.scale = 'scale';
3060    icon.propertyFields.path = 'path';
3061    icon.propertyFields.rotation = 'rotation';
3062
3063    /*
3064		if (typeof data.iconMarkersTooltipTemplate !== 'undefined' && data.iconMarkersTooltipTemplate.trim() !== '') {
3065			icon.tooltipText = data.iconMarkersTooltipTemplate;
3066			icon.tooltipHTML = data.iconMarkersTooltipTemplate;
3067		} else {
3068			icon.tooltipText = data.tooltip.template ? data.tooltip.template : "{tooltipContent}";
3069			icon.tooltipHTML = data.tooltip.template ? data.tooltip.template : "{tooltipContent}";
3070		}*/
3071
3072    icon.propertyFields.horizontalCenter = 'horizontalCenter';
3073    icon.propertyFields.verticalCenter = 'verticalCenter';
3074    icon.propertyFields.fill = 'fill'; // For legend color
3075
3076    markerSeries.fill = data.iconMarkerDefaults.fill; // clickable overlay
3077
3078    clickableOverlay = markerSeriesTemplate.createChild(am4core.Sprite);
3079    clickableOverlay.propertyFields.scale = 'scale';
3080    clickableOverlay.path = 'M-10,0a10,10 0 1,0 20,0a10,10 0 1,0 -20,0';
3081    clickableOverlay.opacity = 0;
3082    clickableOverlay.propertyFields.horizontalCenter = 'horizontalCenter';
3083    clickableOverlay.propertyFields.verticalCenter = 'verticalCenter';
3084
3085    markerSeriesTemplate.tooltipText = data.tooltip.template
3086      ? data.tooltip.template
3087      : '{tooltipContent}';
3088    markerSeriesTemplate.tooltipHTML = data.tooltip.template
3089      ? data.tooltip.template
3090      : '{tooltipContent}';
3091
3092    im.setupTooltip(id, markerSeries, data, clickableOverlay);
3093
3094    if (data.iconMarkerLabels && im.bool(data.iconMarkerLabels.enabled)) {
3095      var markerLabel = markerSeriesTemplate.createChild(am4core.Label);
3096      markerLabel.text =
3097        typeof data.iconMarkerLabels.source !== 'undefined' && data.iconMarkerLabels.source !== ''
3098          ? data.iconMarkerLabels.source
3099          : '{name}';
3100      markerLabel.horizontalCenter = 'middle';
3101      markerLabel.verticalCenter = 'top';
3102      markerLabel.fontSize = data.iconMarkerLabels.fontSize;
3103      markerLabel.nonScaling = false;
3104      markerLabel.fill = data.iconMarkerLabels.fill;
3105      markerLabel.clickable = false;
3106      markerLabel.focusable = false;
3107      markerLabel.hoverable = false;
3108      markerLabel.padding(0, 0, 0, 0);
3109      markerLabel.adapter.add('dy', function (dy, target) {
3110        var icon = target.parent.children.getIndex(0);
3111        var scale = icon.scale;
3112        var space;
3113
3114        if (icon.verticalCenter === 'middle') {
3115          space = 10 * scale;
3116        }
3117
3118        if (icon.verticalCenter === 'bottom') {
3119          space = 0;
3120        }
3121
3122        if (icon.verticalCenter === 'top') {
3123          space = 20 * scale + 5;
3124        } // formula to get the label to distance at an accepted level
3125
3126        return space;
3127      });
3128    } // hover & active
3129
3130    hover = icon.states.create('hover');
3131    hover.propertyFields.fill = 'hover';
3132    active = icon.states.create('active');
3133    active.propertyFields.fill = 'hover'; // highlight - for legend hover
3134
3135    highlightState = icon.states.create('highlight');
3136    highlightState.properties.fill = data.hover;
3137    highlightState.propertyFields.fill = 'hover'; // Set property fields
3138
3139    markerSeriesTemplate.propertyFields.latitude = 'latitude';
3140    markerSeriesTemplate.propertyFields.longitude = 'longitude'; // Add data
3141
3142    // send to front (so on overlay they are always on top of regions)
3143    markerSeries.zIndex = Number.MAX_VALUE;
3144    markerSeries.toFront();
3145
3146    markerSeries.data = data.iconMarkers; // Events
3147
3148    markerSeriesTemplate.events.on('hit', function (ev) {
3149      im.singleHit(id, ev);
3150      im.setupHitEvents(id, ev);
3151    });
3152    markerSeriesTemplate.events.on('over', function (ev) {
3153      im.setupHoverEvents(id, ev);
3154    });
3155  } // enable small map
3156
3157  if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) {
3158    map.smallMap.series.push(markerSeries);
3159  }
3159 // add this series to map series to reference it later if needed
3160
3161  im.maps[id].series.push(markerSeries);
3162  return markerSeries;
3163};
3164
3165iMapsManager.pushLineSeries = function (id, data) {
3166  var im = this,
3167    map = im.maps[id].map,
3168    // shorter reference for the map
3169    lineSeries = {},
3170    lineData = []; // Lines
3171
3172  if (Array.isArray(data.lines) && data.lines.length) {
3173    // Add line series
3174    if (data.projection === 'Orthographic') {
3175      lineSeries = map.series.push(new am4maps.MapLineSeries());
3176      lineSeries.mapLines.template.propertyFields.shortestDistance = true;
3177    } else {
3178      lineSeries = map.series.push(new am4maps.MapArcSeries());
3179      lineSeries.mapLines.template.line.propertyFields.controlPointDistance = 'curvature';
3180      lineSeries.mapLines.template.line.controlPointPosition = 0.5;
3181    }
3182
3183    lineSeries.name =
3184      data.linesLegend && data.linesLegend.title !== '' ? data.linesLegend.title : data.title;
3185    lineSeries.hiddenInLegend = data.linesLegend ? !im.bool(data.linesLegend.enabled) : false;
3186    lineSeries.mapLines.template.nonScalingStroke = true;
3187    lineSeries.mapLines.template.propertyFields.strokeWidth = 'strokeWidth';
3188    lineSeries.mapLines.template.propertyFields.strokeDasharray = 'strokeDash';
3189    lineSeries.mapLines.template.propertyFields.stroke = 'stroke';
3190
3191    // arrow
3192    lineSeries.mapLines.template.arrow.position = 1; // 1 is the end
3193    lineSeries.mapLines.template.arrow.nonScaling = true;
3194    lineSeries.mapLines.template.arrow.propertyFields.fill = 'stroke';
3195    lineSeries.mapLines.template.arrow.horizontalCenter = 'middle';
3196    //lineSeries.mapLines.template.arrow.tooltipHTML = "{title}";
3197
3198    //lineSeries.mapLines.template.arrow.disabled = true;
3199    lineSeries.mapLines.template.arrow.propertyFields.disabled = 'arrowDisabled';
3200
3201    data.lines.forEach(function (lineObj) {
3202      // make sure multiGeoLine is array of arrays:
3203      lineObj.multiGeoLine = [lineObj.multiGeoLine];
3204      lineData.push(lineObj);
3205    });
3206
3207    // events - to do
3208    lineSeries.mapLines.template.events.on('hit', function (ev) {
3209      im.singleHit(id, ev);
3210      im.setupHitEvents(id, ev);
3211    });
3212    lineSeries.mapLines.template.events.on('over', function (ev) {
3213      im.setupHoverEvents(id, ev);
3214    });
3215
3216    lineSeries.data = lineData;
3217
3218    // bring to front
3219    lineSeries.zIndex = Number.MAX_VALUE - 1;
3220    lineSeries.toFront();
3221
3222    // enable small map
3223    if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) {
3224      map.smallMap.series.push(lineSeries);
3225    } //let's hide this from legend, since they don't group in the same Series
3226    //lineSeries.hiddenInLegend = true;
3227    // add this series to map series to reference it later if needed
3228
3229    im.maps[id].series.push(lineSeries); // For legend color
3230
3231    lineSeries.fill = data.lineDefaults.stroke; // if it's part of the parent map
3232
3233    if (id === data.id) {
3234      im.maps[id].allBaseSeries.push(lineSeries);
3235    }
3236  }
3237
3238  return lineSeries;
3239};
3240
3241iMapsManager.pushLabelSeries = function (id, data) {
3242  var im = this,
3243    map = im.maps[id].map,
3244    // shorter reference for the map
3245    labelSeries,
3246    labelSeriesTemplate,
3247    label,
3248    activeState,
3249    highlightState,
3250    hoverState,
3251    background = false;
3252
3253  if (Array.isArray(data.labels) && data.labels.length) {
3254    // Create image series
3255    labelSeries = map.series.push(new am4maps.MapImageSeries());
3256    labelSeries.name =
3257      data.labelsLegend && data.labelsLegend.title !== '' ? data.labelsLegend.title : data.title;
3258    labelSeries.hiddenInLegend = data.labelsLegend ? !im.bool(data.labelsLegend.enabled) : false;
3259
3260    labelSeriesTemplate = labelSeries.mapImages.template;
3261    labelSeriesTemplate.setStateOnChildren = true;
3262
3263    // label
3264    label = labelSeriesTemplate.createChild(am4core.Label);
3265    label.text = '{id}';
3266
3267    //label.nonScaling = true;
3268    label.nonScaling = typeof igmLabelsNonScaling !== 'undefined' ? igmLabelsNonScaling : true;
3269
3270    if (data.labelStyle) {
3271      label.fontFamily = data.labelStyle.fontFamily;
3272      label.fontWeight = data.labelStyle.fontWeight;
3273    }
3274
3275    label.horizontalCenter = data.labelPosition.horizontalCenter;
3276    label.verticalCenter = data.labelPosition.verticalCenter;
3277    label.propertyFields.fill = 'fill';
3278    label.propertyFields.fontSize = 'fontSize';
3279    im.setupTooltip(id, labelSeries, data, label); // custom fields
3280
3281    if (
3282      typeof data.labelsTooltipTemplate !== 'undefined' &&
3283      data.labelsTooltipTemplate.trim() !== ''
3284    ) {
3285      label.tooltipText = data.labelsTooltipTemplate;
3286      label.tooltipHTML = data.labelsTooltipTemplate;
3287    } else {
3288      label.tooltipText =
3289        data.tooltip && data.tooltip.template ? data.tooltip.template : '{tooltipContent}';
3290      label.tooltipHTML =
3291        data.tooltip && data.tooltip.template ? data.tooltip.template : '{tooltipContent}';
3292    }
3293
3294    // Set property fields
3295    labelSeriesTemplate.propertyFields.latitude = 'latitude';
3296    labelSeriesTemplate.propertyFields.longitude = 'longitude';
3297    labelSeriesTemplate.propertyFields.fill = 'fill';
3298    labelSeriesTemplate.propertyFields.fontSize = 'fontSize';
3299    label.propertyFields.verticalCenter = 'verticalCenter';
3300    label.propertyFields.horizontalCenter = 'horizontalCenter';
3301
3302    // labels background - currently only possible through global
3303    background = typeof igmLabelsBackground !== 'undefined' ? igmLabelsBackground : false;
3304    if (typeof background === 'object') {
3305      label.background = new am4core.RoundedRectangle();
3306      label.background.cornerRadius(...background.cornerRadius);
3307      label.background.fill = am4core.color(background.color);
3308      label.padding(...background.padding);
3309      label.background.stroke = am4core.color(background.stroke);
3310    }
3311
3312    // hover & active
3313    hoverState = label.states.create('hover');
3314    hoverState.properties.fill = data.hover;
3315    hoverState.propertyFields.fill = 'hover'; // active
3316
3317    activeState = label.states.create('active');
3318    activeState.properties.fill = data.hover;
3319    activeState.propertyFields.fill = 'hover'; // highlight - for legend hover
3320
3321    highlightState = label.states.create('highlight');
3322    highlightState.properties.fill = data.hover;
3323    highlightState.propertyFields.fill = 'hover';
3324
3325    // send to front (so on overlay they are always on top of regions)
3326    labelSeries.zIndex = Number.MAX_VALUE;
3327    labelSeries.toFront();
3328
3329    // Add data
3330    labelSeries.data = data.labels; // For legend color
3331
3332    labelSeries.fill = data.labelDefaults.fill; // Events
3333
3334    labelSeriesTemplate.events.on('hit', function (ev) {
3335      im.singleHit(id, ev);
3336      im.setupHitEvents(id, ev);
3337    });
3338    labelSeriesTemplate.events.on('over', function (ev) {
3339      im.setupHoverEvents(id, ev);
3340    });
3341  } // enable small map
3342
3343  if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) {
3344    map.smallMap.series.push(labelSeries);
3345  } // add this series to map series to reference it later if needed
3346
3347  im.maps[id].labelSeries.push(labelSeries);
3348  im.maps[id].series.push(labelSeries); // if part of the parent map
3349
3350  if (id === data.id) {
3351    im.maps[id].allBaseSeries.push(labelSeries);
3352  }
3353
3354  return labelSeries;
3355};
3356
3357iMapsManager.setupTooltip = function (id, series, data, marker) {
3358  var im = this,
3359    tooltip = data.tooltip,
3360    map = im.maps[id].map,
3361    shadow;
3362  marker = marker || false; // don't include it in export
3363  // regionSeries.tooltip.exportable = false;
3364  // default behaviour
3365
3366  if (typeof data.tooltip === 'undefined') {
3367    series.tooltip.disabled = false;
3368    series.tooltip.getFillFromObject = false;
3369    series.tooltip.getStrokeFromObject = false;
3370    series.tooltip.label.fill = am4core.color('#000000');
3371    series.tooltip.background.fill = am4core.color('#FFFFFF');
3372    return;
3373  }
3374
3375  if (!im.bool(data.tooltip.enabled)) {
3376    series.tooltip.disabled = true;
3377    return series;
3378  }
3379
3380  // if it's disabled on mobile/smaller screens
3381  if (
3382    _typeof(data.tooltip.disableMobile) !== undefined &&
3383    im.bool(data.tooltip.disableMobile) &&
3384    window.innerWidth <= 780
3385  ) {
3386    series.tooltip.disabled = true;
3387    return series;
3388  }
3389
3390  //if it's overlay, it might have a custom config
3391
3392  // tooltip settings from map config
3393  series.tooltip.label.interactionsEnabled = im.bool(tooltip.fixed);
3394  series.tooltip.background.cornerRadius = tooltip.cornerRadius;
3395  if (tooltip.pointerLength) {
3396    series.tooltip.background.pointerLength = parseInt(tooltip.pointerLength);
3397  }
3398  series.tooltip.getFillFromObject = false;
3399  series.tooltip.getStrokeFromObject = false;
3400  series.tooltip.label.fill = tooltip.color;
3401  series.tooltip.background.fill = tooltip.backgroundColor;
3402  series.tooltip.fontFamily = tooltip.fontFamily;
3403  series.tooltip.fontSize = tooltip.fontSize;
3404  series.tooltip.fontWeight = tooltip.fontWeight;
3405
3406  // animation - disable the fly out effect on markers
3407  series.tooltip.animationDuration = 0;
3408
3409  // border
3410  if (tooltip.borderColor !== 'undefined') {
3411    series.tooltip.background.stroke = tooltip.borderColor;
3412    series.tooltip.background.strokeWidth = tooltip.borderWidth;
3413  }
3414
3415  if (typeof tooltip.maxWidth !== 'undefined' && tooltip.maxWidth !== '') {
3416    series.tooltip.maxWidth = parseInt(tooltip.maxWidth);
3417    series.tooltip.contentWidth = parseInt(tooltip.maxWidth);
3418  }
3419  series.tooltip.label.wrap = true;
3420
3421  // box-shadow
3422  if (typeof tooltip.customShadow !== 'undefined' && im.bool(tooltip.customShadow)) {
3423    shadow = series.tooltip.background.filters.getIndex(0);
3424    shadow.dx = tooltip.customShadowOpts.dx;
3425    shadow.dy = tooltip.customShadowOpts.dy;
3426    shadow.blur = tooltip.customShadowOpts.blur;
3427    shadow.opacity = tooltip.customShadowOpts.opacity;
3428    shadow.color = tooltip.customShadowOpts.color;
3429  }
3430
3431  // Set up fixed tooltips
3432  if (im.bool(tooltip.fixed)) {
3433    if (tooltip.showTooltipOn === 'always') {
3434      //allow tooltip to be hidden outside viewport
3435      series.tooltip.ignoreBounds = true;
3436
3437      series.tooltip.pointerOrientation = (function (toolTipPosition) {
3438        switch (toolTipPosition) {
3439          case 'horizontal':
3440            return 'horizontal';
3441          case 'below':
3442            return 'up';
3443          case 'above':
3444            return 'down';
3445          case 'right':
3446            return 'left';
3447          case 'left':
3448            return 'right';
3449          default:
3450            return 'vertical';
3451        }
3452      })(tooltip.toolTipPosition);
3453    }
3454
3455    if (series.mapPolygons) {
3456      series.calculateVisualCenter = true;
3457      series.mapPolygons.template.tooltipPosition = 'fixed';
3458
3459      // in case it's a group and the tooltip is set to fixed,
3460      // we need to set the tooltip hover to have the regions keep the hover state
3461      if (series.groupHover) {
3462        // set to false so that the tooltip does not
3463        // inherit the highlight state color on hover
3464        series.tooltip.getFillFromObject = false;
3465        series.tooltip.events.on('over', function (ev) {
3466          ev.target.dataItem.component.mapPolygons.each(function (polygon) {
3467            polygon.setState('highlight');
3468          });
3469        });
3470        series.tooltip.events.on('out', function (ev) {
3471          ev.target.dataItem.component.mapPolygons.each(function (polygon) {
3472            polygon.setState('default');
3473          });
3474        });
3475      }
3476
3477      series.tooltip.keepTargetHover = true;
3478
3479      if (tooltip.showTooltipOn) {
3480        series.mapPolygons.template.showTooltipOn = tooltip.showTooltipOn;
3481
3482        // to have the tooltip in the marker fixed, even after over out
3483        if (tooltip.showTooltipOn === 'hit') {
3484          series.tooltip.keepTargetHover = false;
3485        }
3486      }
3487    } else {
3488      // for markers
3489      series.mapImages.template.tooltipPosition = 'fixed';
3490      series.tooltip.keepTargetHover = true;
3491
3492      if (tooltip.showTooltipOn && marker) {
3493        series.mapImages.template.showTooltipOn = tooltip.showTooltipOn;
3494        marker.showTooltipOn = tooltip.showTooltipOn;
3495
3496        // to have the tooltip in the marker fixed, even after over out
3497        if (tooltip.showTooltipOn === 'hit') {
3498          series.tooltip.keepTargetHover = false;
3499        }
3500
3501        // to have the tooltip display after the map loads
3502        if (tooltip.showTooltipOn === 'always') {
3503          map.events.on('appeared', function () {
3504            marker.clones.each(function (clone) {
3505              clone.showTooltip();
3506            });
3507          });
3508
3509          map.events.on('mappositionchanged', function () {
3510            marker.clones.each(function (clone) {
3511              clone.showTooltip();
3512            });
3513          });
3514        }
3515      }
3516    }
3517  }
3518  return series;
3519};
3520
3521iMapsManager.prepareURL = function (str) {
3522  if (typeof str !== 'string') {
3523    return str;
3524  }
3525
3526  // parse html entities
3527  str = str.replace(/&amp;/gi, '&');
3528  str.replace(/&#(\d+);/g, function (match, dec) {
3529    return String.fromCharCode(dec);
3530  });
3531
3532  // check if allowed url
3533  var url, protocols;
3534  try {
3535    url = new URL(str);
3536  } catch {
3537    url = false;
3538  }
3539
3540  // check if valid url. If string it might be a anchor or relative path, so keep going
3541  if (url) {
3542    // accepted protocols
3543    protocols = [null, 'http:', 'https:', 'mailto:', 'tel:'];
3544    if (!protocols.includes(url.protocol)) {
3545      console.log('URL protocol not allowed');
3546      return '';
3547    }
3548  }
3549
3550  return str;
3551};
3552
3553iMapsManager.setupHitEvents = function (id, ev) {
3554  var im = this,
3555    data = im.maps[id].data,
3556    dataContext,
3557    map = im.maps[id].map,
3558    customActionName,
3559    targetType = im.getTargetSeriesType(ev.target),
3560    clicked = im.maps[id].clicked || false,
3561    zoomCluster = data.clusterMarkers ? parseFloat(data.clusterMarkers.zoomLevel) : 1,
3562    currentRegion,
3563    mapFunction,
3564    container = document.getElementById(data.container),
3565    event = new Event('mapEntryClicked');
3566
3567  if (ev.target.isLabels) {
3568    dataContext = ev.target.dataItems.first.dataContext;
3569  } else {
3570    dataContext = ev.target.dataItem.dataContext;
3571  }
3572
3573  // on certain devices, the click gets triggered twice, so the 'hold click actions' won't work properly
3574  // but we minize the issue by allowing it to run anyway - so on devices where the tap runs twice, the hold won't work at the moment
3575  if (map.lastClickedEntry === ev.target && !im.maps[id].clicked) {
3576    // the clicked is used to control the "hold click actions" feature
3577    im.maps[id].clicked = dataContext;
3578
3579    // we reset after 1 second, to allow real second clicks to run
3580    // this also has the side effect that when the 'hold click actions' is enabled, only after 1 second, the second tap will actually work
3581    setTimeout(function () {
3582      map.lastClickedEntry = null;
3583      im.maps[id].clicked = null;
3584    }, 1000);
3585
3586    return;
3587  }
3588
3589  // we reset it to null, in case it was clicked before, double tap
3590  im.maps[id].clicked = null;
3591  map.lastClickedEntry = ev.target;
3592  container.dispatchEvent(event);
3593
3594  // if it's a cluster, zoom in a bit
3595  if (targetType === 'MapImage') {
3596    if (dataContext.cluster) {
3597      // if we're far from the max, let's just zoom half
3598      if (zoomCluster - parseInt(map.zoomLevel) > 5) {
3599        zoomCluster = parseInt(map.zoomLevel) + zoomCluster / 2;
3600      }
3601      ev.target.series.chart.zoomToMapObject(ev.target, zoomCluster);
3602    }
3603  } // for admin log
3604
3605  console.log(dataContext); // Zoom on click
3606
3607  if (data.zoom && im.bool(data.zoom.enabled) && im.bool(data.zoom.zoomOnClick)) {
3608    ev.zooming = true;
3609    im.zoomToRegion(ev, id);
3610  } // drill down
3611
3612  if (targetType === 'MapPolygon' && im.bool(data.drillDownOnClick)) {
3613    im.drillDown(id, ev);
3614  }
3615
3616  if (dataContext.madeFromGeoData) {
3617    // check if we should clear selected or not?
3618    // iMapsManager.clearSelected(id);
3619
3620    mapFunction = 'igm_inactive_' + data.id;
3621
3622    if (typeof window[mapFunction] === 'function') {
3623      data = window[mapFunction](data);
3624    }
3625
3626    return;
3627  }
3628
3629  // if it's a marker with action to open specific map
3630  if (
3631    targetType === 'MapImage' &&
3632    _typeof(dataContext.action) !== undefined &&
3633    dataContext.action === 'igm_display_map'
3634  ) {
3635    if (Array.isArray(iMapsManager.maps[id].seriesById[parseInt(dataContext.content)])) {
3636      currentRegion = iMapsManager.maps[id].seriesById[parseInt(dataContext.content)];
3637      im.drillTo(id, ev, currentRegion, true);
3638    }
3639  } // if admin, we don't trigger the actions
3640
3641  if (im.bool(data.admin)) {
3642    return;
3643  } // if we need to hold action on mobile/smaller screens to show tooltip
3644
3645  if (
3646    data.tooltip &&
3647    _typeof(data.tooltip.enabled) !== undefined &&
3648    im.bool(data.tooltip.enabled) &&
3649    _typeof(data.tooltip.disableMobile) !== undefined &&
3650    !im.bool(data.tooltip.disableMobile) &&
3651    _typeof(data.tooltip.holdAction) !== undefined &&
3652    im.bool(data.tooltip.holdAction) &&
3653    window.innerWidth <= 780
3654  ) {
3655    if (!clicked || clicked !== dataContext) {
3656      console.log('Holding action for second tap.');
3657      im.maps[id].clicked = dataContext;
3658      return;
3659    }
3660
3661    // in case the previously clicked will now perform action, we reset it
3662    if (clicked === dataContext) {
3663      im.maps[id].clicked = false;
3664    } // if it's a different one, set it normally
3665    else {
3666      im.maps[id].clicked = dataContext;
3667    }
3668  }
3669
3670  if (dataContext.action === 'none') {
3671    // do nothing
3672    return;
3673  }
3674
3675  // check urls
3676  if (dataContext.action === 'open_url' || dataContext.action === 'open_url_new') {
3677    dataContext.content = iMapsManager.prepareURL(dataContext.content);
3678  }
3679  // open new url
3680  if (dataContext.action === 'open_url' && dataContext.content !== '') {
3681    document.location = dataContext.content;
3682  } else if (dataContext.action === 'open_url_new' && dataContext.content !== '') {
3683    window.open(dataContext.content);
3684  } // custom
3685  else if (dataContext.action && dataContext.action.includes('custom')) {
3686    customActionName = dataContext.action + '_' + dataContext.mapID;
3687
3688    if (typeof window[customActionName] !== 'undefined') {
3689      window[customActionName](dataContext);
3690    }
3691  } else {
3692    if (typeof window[dataContext.action] !== 'undefined') {
3693      window[dataContext.action](id, dataContext);
3694    }
3695  }
3696};
3697
3698iMapsManager.zoomToMap = function (ev, target) {
3699  var im = this;
3700  var baseMap = false;
3701
3702  target.forEach(function (series) {
3703    if ('MapPolygonSeries' == im.getTargetSeriesType(series)) {
3704      baseMap = series;
3705      return;
3706    }
3707  });
3708
3709  if (baseMap) {
3710    ev.target.series.chart.zoomToRectangle(
3711      baseMap.north,
3712      baseMap.east,
3713      baseMap.south,
3714      baseMap.west,
3715      1,
3716      true
3717    );
3718    console.log('zoomed to specific map');
3719    return;
3720  }
3721  console.log('failed to zoom to specific map');
3722};
3723
3724iMapsManager.zoomToRegion = function (ev, id) {
3725  var im = this,
3726    seriesType = im.getTargetSeriesType(ev.target),
3727    data = im.maps[id].data,
3728    markerZoomLevel,
3729    dataContext; // do nothing if we clicked a label
3730
3731  if (ev.target.isLabels) {
3732    return;
3733  }
3734
3735  dataContext = ev.target.dataItem.dataContext; // if it's a marker, handle it differently
3736
3737  if (seriesType == 'MapImage') {
3738    // if it's a cluster marker
3739    if (dataContext.cluster) {
3740      //do nothing, we already zoomed
3741    } else {
3742      markerZoomLevel =
3743        typeof igmMarkerZoomLevelOnClick !== 'undefined'
3744          ? igmMarkerZoomLevelOnClick
3745          : ev.target.parent.chart.zoomLevel * 2;
3746      ev.target.series.chart.zoomToMapObject(ev.target, markerZoomLevel, true);
3747    }
3748  } else {
3749    if (dataContext.id === 'asia' && !data.map.startsWith('continents')) {
3750      ev.target.series.chart.deltaLongitudeOriginal = ev.target.series.chart.deltaLongitude;
3751      ev.target.series.chart.deltaLongitude = -10;
3752      ev.target.series.chart.zoomToGeoPoint(
3753        {
3754          latitude: 34.076842,
3755          longitude: 100.693068,
3756        },
3757        1.7,
3758        true
3759      );
3760    } else if (dataContext.id === 'asia' && data.map.startsWith('continents')) {
3761      ev.target.series.chart.zoomToGeoPoint(
3762        {
3763          latitude: 34.076842,
3764          longitude: 100.693068,
3765        },
3766        2.2,
3767        true
3768      );
3769    } else if (
3770      dataContext.id === 'northAmerica' &&
3771      data.map.startsWith('region/world/worldRegion')
3772    ) {
3773      ev.target.series.chart.zoomToGeoPoint(
3774        {
3775          latitude: 55.5,
3776          longitude: -105.5,
3777        },
3778        3,
3779        true
3780      );
3781    } else if (dataContext.id === 'northAmerica' && data.map.startsWith('continents')) {
3782      ev.target.series.chart.zoomToGeoPoint(
3783        {
3784          latitude: 55.5,
3785          longitude: -105.5,
3786        },
3787        2.3,
3788        true
3789      );
3790    } else if (dataContext.id === 'ZA' && data.map.startsWith('world')) {
3791      ev.target.series.chart.zoomToGeoPoint(
3792        {
3793          latitude: -28.6,
3794          longitude: 24.7,
3795        },
3796        12.2,
3797        true
3798      );
3799    } else if (dataContext.id === 'US' && data.map.startsWith('world')) {
3800      ev.target.series.chart.zoomToGeoPoint(
3801        {
3802          latitude: 49,
3803          longitude: -119,
3804        },
3805        2.6,
3806        true
3807      );
3808    } else if (dataContext.id === 'RU' && data.map.startsWith('world')) {
3809      ev.target.series.chart.zoomToGeoPoint(
3810        {
3811          latitude: 64.5,
3812          longitude: 105,
3813        },
3814        2.1,
3815        true
3816      );
3817    } else {
3818      if (typeof ev.target.series.chart.deltaLongitudeOriginal !== 'undefined') {
3819        ev.target.series.chart.deltaLongitude = ev.target.series.chart.deltaLongitude;
3820      }
3821
3822      ev.target.series.chart.zoomToMapObject(ev.target, ev.target.zoomLevel * 2);
3823    }
3824  }
3825};
3826
3827iMapsManager.setupHoverEvents = function (id, ev) {
3828  var im = this,
3829    selected = im.maps[id].selected || false,
3830    dataContext;
3831
3832  if (ev.target.isLabels) {
3833    dataContext = ev.target.dataItems.first.dataContext;
3834  } else {
3835    dataContext = ev.target.dataItem.dataContext;
3836  }
3837
3838  // when tooltip position is fixed, hovering regions might leave hovered state behind, when the overout goes through the tooltip
3839  if (ev.target.className !== 'MapImage' && ev.target.tooltipPosition === 'fixed') {
3840    if (Array.isArray(im.tempHover) && im.tempHover.length >= 0) {
3841      im.tempHover[0].setState('default');
3842      im.tempHover = [];
3843    }
3844    im.tempHover = [ev.target];
3845  }
3846
3847  if (dataContext.action && dataContext.action !== 'none') {
3848    ev.target.cursorOverStyle = am4core.MouseCursorStyle.pointer;
3849  }
3850
3851  if (
3852    Array.isArray(selected) &&
3853    !selected.includes(ev.target) &&
3854    ev.target.dataItem &&
3855    typeof ev.target.dataItem.dataContext.madeFromGeoData === 'undefined'
3856  ) {
3857    selected.forEach(function (sel) {
3858      if (typeof sel === 'object' && typeof sel.isHover !== 'undefined') {
3859        sel.isHover = false;
3860      }
3861    });
3862  }
3863
3864  // we exclude touch devices, since the hover event is also triggered on tap, otherwise we have 2 select events triggered
3865  if (
3866    im.bool(dataContext.triggerClickOnHover) &&
3867    (!iMapsManager.isTouchScreenDevice() || ev.type === 'over') &&
3868    (typeof iMaps.maps[id].mapClicked === 'undefined' || iMaps.maps[id].mapClicked === false)
3869  ) {
3870    iMapsManager.select(id, dataContext.id, false, true, dataContext.mapID, false);
3871  }
3872
3873  // if it's a marker and we want to trigger hover event also on associated regions in marker value
3874  if (im.bool(dataContext.triggerRegionHover) && dataContext.val && dataContext.val !== '') {
3875    iMapsManager.hover(id, dataContext.val);
3876    ev.target.events.on('out', function () {
3877      iMapsManager.clearHovered(id);
3878    });
3879  }
3880};
3881
3882iMapsManager.singleHit = function (id, ev) {
3883  var im = this,
3884    dataContext,
3885    data = im.maps[id].data;
3886
3887  if (ev.target.isLabels) {
3888    dataContext = ev.target.dataItems.first.dataContext;
3889  } else {
3890    dataContext = ev.target.dataItem.dataContext;
3891  }
3892
3893  if (dataContext.madeFromGeoData) {
3894    return;
3895  }
3896
3897  if (iMaps.maps[id].activeStateControl === true && dataContext.activeState) {
3898    iMaps.maps[id].mapClicked = true;
3899  }
3900
3901  // causes issues on tap on mobile, we need to click twice?
3902  // seems the clear selected is affecting
3903
3904  // update:
3905  // let's try passing second argument as false if tooltip is set to display on click?
3906  let keepThis = true;
3907  if (data.tooltip && data.tooltip.showTooltipOn === 'hit') {
3908    keepThis = false;
3909  }
3910  iMapsManager.clearSelected(id, keepThis, true);
3911
3912  ev.target.isActive = true;
3913  ev.target.isHover = true;
3914  ev.target.setState('active');
3915  im.maps[id].selected = [ev.target];
3916};
3917
3918iMapsManager.groupHit = function (id, ev) {
3919  var im = this,
3920    selected = im.maps[id].selected || false;
3921
3922  if (ev.target.dataItem.dataContext.madeFromGeoData) {
3923    return;
3924  }
3925
3926  if (iMaps.maps[id].activeStateControl === true && ev.target.dataItem.dataContext.activeState) {
3927    iMaps.maps[id].mapClicked = true;
3928  }
3929
3930  //removed to improve click on group ?
3931  iMapsManager.clearSelected(id);
3932
3933  selected = [];
3934
3935  ev.target.parent.mapPolygons.each(function (polygon) {
3936    // added the typeof control to only run code on original clicked/hit entry and not the individual shape
3937    // removed, because the code wasn't running otherwise -> && typeof polygon.dataItem.dataContext.originalID === 'undefined'
3938    if (!polygon.dataItem.dataContext.madeFromGeoData) {
3939      // toggle active state
3940      polygon.setState('active');
3941
3942      if (ev.target === polygon) {
3943        polygon.isHover = true;
3944      }
3945
3946      //removed to fix bug when hovering groups after clicked ?
3947      polygon.isActive = true;
3948      polygon.isGroupActive = true;
3949      selected.push(polygon);
3950    }
3951  });
3952
3953  im.maps[id].selected = selected;
3954};
3955
3956iMapsManager.groupHover = function (id, ev) {
3957  var im = this,
3958    dataContext;
3959
3960  if (ev.target.isLabels) {
3961    dataContext = ev.target.dataItems.first.dataContext;
3962  } else {
3963    dataContext = ev.target.dataItem.dataContext;
3964  }
3965
3966  if (ev.target.dataItem.dataContext.madeFromGeoData) {
3967    return;
3968  }
3969
3970  if (
3971    im.bool(dataContext.triggerClickOnHover) &&
3972    (typeof iMaps.maps[id].mapClicked === 'undefined' || iMaps.maps[id].mapClicked === false)
3973  ) {
3974    iMapsManager.select(id, dataContext.id, false, true, dataContext.mapID, false);
3975  } // set mouse hover pointer cursor
3976
3977  if (ev.target.dataItem.dataContext.action && ev.target.dataItem.dataContext.action != 'none') {
3978    ev.target.cursorOverStyle = am4core.MouseCursorStyle.pointer;
3979  } // highlight all polygons from this group
3980
3981  ev.target.parent.mapPolygons.each(function (polygon) {
3982    if (!polygon.dataItem.dataContext.madeFromGeoData) {
3983      if (!polygon.isActive) {
3984        polygon.setState('highlight');
3985      }
3986    }
3987  });
3988};
3989
3990iMapsManager.groupHoverOut = function (id, ev) {
3991  if (ev.target.dataItem.dataContext.madeFromGeoData) {
3992    return;
3993  }
3994
3995  ev.target.parent.mapPolygons.each(function (polygon) {
3996    if (!polygon.isGroupActive) {
3997      polygon.setState('default');
3998      polygon.isActive = false;
3999      polygon.isHover = false;
4000    }
4001  });
4002};
4003
4004/**
4005 * Get regions object collection based on value of a specific parameter
4006 * id - map id
4007 * value - value you want to filter by
4008 * parameter - parameter to search
4009 */
4010iMapsManager.getRegionsByValue = function (id, value, parameter) {
4011  var im = this,
4012    map = im.maps[id],
4013    series = map.series,
4014    seriesType = false,
4015    regions = [],
4016    tempArray;
4017
4018  value = value || '*';
4019  parameter = parameter || 'val';
4020
4021  series.forEach(function (serie) {
4022    seriesType = im.getTargetSeriesType(serie);
4023    if (seriesType === 'MapPolygonSeries') {
4024      tempArray = serie.mapPolygons.values.filter(function (entry) {
4025        if (entry.dataItem.dataContext[parameter] === value || value === '*') {
4026          return true;
4027        }
4028      });
4029      regions = regions.concat(tempArray);
4030    }
4031  });
4032
4033  return regions;
4034};
4035
4036/** Get markers object collection based on value of a specific parameter
4037 * It will search for all kinds of markers (icons, images, round markers)
4038 * id - map id
4039 * value - value you want to filter by
4040 * parameter - parameter to search
4041 */
4042iMapsManager.getMarkersByValue = function (id, value, parameter) {
4043  var im = this,
4044    map = im.maps[id],
4045    series = map.series,
4046    seriesType = false,
4047    markers = [],
4048    tempArray;
4049
4050  value = value || '*';
4051  parameter = parameter || 'val';
4052
4053  series.forEach(function (serie) {
4054    seriesType = im.getTargetSeriesType(serie);
4055    if (seriesType === 'MapImageSeries') {
4056      tempArray = serie.mapImages.values.filter(function (entry) {
4057        if (entry.dataItem.dataContext[parameter] === value || value === '*') {
4058          return true;
4059        }
4060      });
4061      markers = markers.concat(tempArray);
4062    }
4063  });
4064
4065  return markers;
4066};
4067
4068/**
4069 * Selects a element in the map
4070 * id - id of the map
4071 * elID - id of the element to select
4072 * forceFixedTooltip - if we should force the tooltip to be fixed or not
4073 * showTooltip
4074 * seriesMapID - map id of the series, which could be ab overlay
4075 * click - boolean - trigger true click event
4076 */
4077
4078iMapsManager.select = function (id, elID, forceFixedTooltip, showTooltip, seriesMapID, click) {
4079  var im = this,
4080    map = im.maps[id],
4081    data = im.maps[id].data,
4082    series = map.series.slice().reverse(), // we clone and reverse so that the grouped region series are at the end, which solves a particular bug with the select() function and the tooltip
4083    selected = [],
4084    select,
4085    group,
4086    defaultTooltipPosition,
4087    defaultTooltipShowOn,
4088    seriesByID = iMaps.maps[id].seriesById,
4089    thisSeries = false,
4090    customRegionGroup = true,
4091    triggered = false, // temp solution to prevent map from triggering multiple click actions if there are entries with same ID in different layers
4092    keepThis = true;
4093
4094  // map sure it's string
4095  if (Number.isInteger(elID)) {
4096    elID = elID.toString();
4097  }
4098
4099  // Force fixed position?
4100  if (typeof forceFixedTooltip === 'undefined') {
4101    forceFixedTooltip = true;
4102  }
4103
4104  if (typeof showTooltip === 'undefined') {
4105    showTooltip = true;
4106  }
4107
4108  if (typeof seriesMapID === 'undefined') {
4109    seriesMapID = id;
4110  }
4111
4112  if (seriesByID.hasOwnProperty(seriesMapID)) {
4113    thisSeries = seriesByID[seriesMapID];
4114  }
4115
4116  if (typeof click === 'undefined') {
4117    click = true;
4118  } else {
4119    click = false;
4120  }
4121
4122  // we pass the click as the 'skipReset' - if not a real click, its the hover and we skip the reset to prevent flickering
4123  // second argument, the keepThis set to true, to avoid removing hover status of selected/hovered entry
4124  // update - second argument might be set to false, if tooltip only displays on hit to fix issue where the tooltip wouldn't close
4125  if (data.tooltip && data.tooltip.showTooltipOn === 'hit') {
4126    keepThis = false;
4127  }
4128
4129  iMapsManager.clearSelected(id, keepThis, !click);
4130
4131  if (click) {
4132    iMaps.maps[id].activeStateControl = true;
4133  } else {
4134    iMaps.maps[id].activeStateControl = false;
4135  }
4136
4137  // if we have to search in specific series and this is not that one, skip
4138  if (thisSeries) {
4139    series = thisSeries;
4140  }
4141
4142  if (Array.isArray(series)) {
4143    for (var i = 0, len = series.length; i < len; i++) {
4144      if (triggered) {
4145        break;
4146      }
4147
4148      // if it's one of the cluster series, ignore
4149      if (typeof series[i].isCluster !== 'undefined' && series[i].isCluster) {
4150        continue;
4151      }
4152      // regionSeries
4153      if (series[i].mapPolygons) {
4154        //first trigger click
4155        // this might return false if there is no entry created for this group and we are selecting programmatically
4156        select = series[i].getPolygonById(elID);
4157
4158        if (
4159          select &&
4160          typeof select.dataItem.dataContext.originalID !== 'undefined' &&
4161          select.dataItem.dataContext.originalID.includes(',')
4162        ) {
4163          elID = select.dataItem.dataContext.originalID;
4164        }
4165
4166        // show series if it's hidden
4167        if (select) {
4168          series[i].show();
4169        }
4170
4171        // check if group
4172        if (elID.includes(',')) {
4173          if (typeof select !== 'undefined' && select) {
4174            select.tooltip = false;
4175            select.dispatchImmediately('hit');
4176            select.hideTooltip();
4177
4178            customRegionGroup = false;
4179
4180            // reset true click events controller
4181            iMaps.maps[id].activeStateControl = true;
4182          }
4183
4184          //then highlight the first one
4185          group = elID.split(',');
4186          group.forEach(function (rxid, indx) {
4187            select = series[i].getPolygonById(rxid.trim());
4188
4189            if (typeof select !== 'undefined' && select) {
4190              if (select.dataItem.dataContext.madeFromGeoData) {
4191                return;
4192              }
4193
4194              // if originalID is not the same as the current originalID from the group, let's ignore it, it could belong to another group
4195              if (select.dataItem.dataContext.originalID !== elID && !customRegionGroup) {
4196                return;
4197              }
4198
4199              //the first one, let's trigger the tooltip and the hit
4200              if (indx === 0 && showTooltip) {
4201                triggered = true;
4202                if (forceFixedTooltip) {
4203                  defaultTooltipPosition =
4204                    typeof select !== 'undefined' && typeof select.tooltipPosition !== 'undefined'
4205                      ? select.tooltipPosition
4206                      : 'hover';
4207                  select.tooltipPosition = 'fixed';
4208
4209                  // experimental code to check if on hit tooltip remains
4210                  defaultTooltipShowOn = select.showTooltipOn;
4211                  select.showTooltipOn = 'always';
4212
4213                  iMaps.maps[id].activeStateControl = true;
4214
4215                  select.tooltipPosition = defaultTooltipPosition;
4216                  select.showTooltipOn = defaultTooltipShowOn;
4217
4218                  select.selectedFromGroup = true;
4219                } else {
4220                  select.isHover = true;
4221                }
4222              }
4223
4224              // if we have the active state, use it instead
4225              if (data.regionActiveState && im.bool(data.regionActiveState.enabled)) {
4226                select.setState('active');
4227              } else {
4228                select.setState('highlight');
4229              }
4230
4231              selected.push(select);
4232            }
4233          });
4234        } else {
4235          // individual region
4236          select = series[i].getPolygonById(elID);
4237
4238          if (typeof select !== 'undefined' && select) {
4239            let timeout = 0;
4240            //check if globe to rotate
4241            if (data.projection === 'Orthographic') {
4242              let map = iMaps.maps[id].map;
4243              map.animate(
4244                {
4245                  property: 'deltaLongitude',
4246                  to: -select.longitude,
4247                },
4248                500,
4249                am4core.ease.linear
4250              );
4251              map.animate(
4252                {
4253                  property: 'deltaLatitude',
4254                  to: -select.latitude,
4255                },
4256                550,
4257                am4core.ease.linear
4258              );
4259              timeout = 550;
4260            }
4261
4262            setTimeout(
4263              function (select) {
4264                console.log(select.dataItem.dataContext);
4265
4266                if (
4267                  forceFixedTooltip &&
4268                  select.dataItem.dataContext &&
4269                  select.dataItem.dataContext.action !== 'igm_display_map'
4270                ) {
4271                  defaultTooltipPosition =
4272                    typeof select !== 'undefined' && typeof select.tooltipPosition !== 'undefined'
4273                      ? select.tooltipPosition
4274                      : 'hover';
4275                  select.tooltipPosition = 'fixed';
4276                  // experimental code to check if on hit tooltip remains
4277                  defaultTooltipShowOn = select.showTooltipOn;
4278                  select.showTooltipOn = 'always';
4279                  select.dispatchImmediately('hit');
4280                  triggered = true;
4281                  select.tooltipPosition = defaultTooltipPosition;
4282                  select.showTooltipOn = defaultTooltipShowOn;
4283                  selected.push(select);
4284                }
4285                // if action is set to display map it will zoom and we don't need to show tooltip
4286                else if (
4287                  select.dataItem.dataContext &&
4288                  select.dataItem.dataContext.action === 'igm_display_map'
4289                ) {
4290                  select.dispatchImmediately('hit');
4291                  select.hideTooltip();
4292                  triggered = true;
4293                  selected.push(select);
4294                } else {
4295                  select.dispatchImmediately('hit');
4296                  triggered = true;
4297                  selected.push(select);
4298                }
4299              },
4300              timeout,
4301              select
4302            );
4303          }
4304        }
4305      }
4306      // imageSeries
4307      if (series[i].mapImages) {
4308        // multiple markers
4309        if (elID.includes(',')) {
4310          // hilight
4311          group = elID.split(',');
4312          group.forEach(function (rxid) {
4313            select = series[i].getImageById(rxid);
4314
4315            if (typeof select !== 'undefined' && select) {
4316              // show series if it's hidden
4317              series[i].show();
4318
4319              if (forceFixedTooltip) {
4320                defaultTooltipShowOn = select.showTooltipOn;
4321                select.showTooltipOn = 'always';
4322
4323                defaultTooltipPosition =
4324                  typeof select && typeof select.tooltipPosition !== 'undefined'
4325                    ? select.tooltipPosition
4326                    : 'hover';
4327                select.tooltipPosition = 'fixed';
4328                select.dispatchImmediately('hit');
4329                triggered = true;
4330                select.tooltipPosition = defaultTooltipPosition;
4331                select.showTooltipOn = defaultTooltipShowOn;
4332                selected.push(select);
4333              } else {
4334                select.dispatchImmediately('hit');
4335                triggered = true;
4336                selected.push(select);
4337              }
4338            }
4339          });
4340        }
4341        // single marker
4342        else {
4343          let selectMarker = series[i].getImageById(elID);
4344          if (selectMarker) {
4345            // show series if it's hidden
4346            series[i].show();
4347
4348            let timeoutM = 0;
4349            //check if globe to rotate
4350            if (data.projection === 'Orthographic') {
4351              let map = iMaps.maps[id].map;
4352              map.animate(
4353                {
4354                  property: 'deltaLongitude',
4355                  to: -selectMarker.longitude,
4356                },
4357                500,
4358                am4core.ease.linear
4359              );
4360              map.animate(
4361                {
4362                  property: 'deltaLatitude',
4363                  to: -selectMarker.latitude,
4364                },
4365                550,
4366                am4core.ease.linear
4367              );
4368              timeoutM = 550;
4369            }
4370
4371            setTimeout(function () {
4372              if (forceFixedTooltip) {
4373                defaultTooltipPosition = selectMarker.tooltipPosition;
4374                defaultTooltipShowOn = selectMarker.showTooltipOn;
4375                selectMarker.tooltipPosition = 'fixed';
4376                selectMarker.showTooltipOn = 'always';
4377                selectMarker.isHover = true;
4378                selectMarker.isActive = true;
4379                // to also show tooltip when using the select method
4380                selectMarker.dispatchImmediately('hit');
4381                selectMarker.setState('active');
4382                selectMarker.children.each(function (child) {
4383                  //if(child.className === 'Circle'){
4384                  child.showTooltip(0);
4385                  //}
4386                });
4387                triggered = true;
4388                selectMarker.tooltipPosition = defaultTooltipPosition;
4389                selectMarker.showTooltipOn = defaultTooltipShowOn;
4390                selected.push(selectMarker);
4391              } else {
4392                selectMarker.dispatchImmediately('hit');
4393                triggered = true;
4394                selected.push(selectMarker);
4395              }
4396            }, timeoutM);
4397          }
4398        }
4399      }
4400    }
4401  }
4402
4403  im.maps[id].selected = selected;
4404  return select;
4405};
4406
4407iMapsManager.setupRangeHeatMap = function (series, id, data) {
4408  var im = this,
4409    regions = data.regions,
4410    markers = data.roundMarkers,
4411    reordered,
4412    seriesType = im.getTargetSeriesType(series);
4413
4414  if (seriesType === 'MapImageSeries') {
4415    if (!Array.isArray(data.heatMapMarkers.range) || data.heatMapMarkers.range.length === 0) {
4416      return;
4417    }
4418
4419    // reorder ranges by value
4420    reordered = data.heatMapMarkers.range.slice(0);
4421    reordered.sort(function (a, b) {
4422      if (isNaN(a.rule)) {
4423        var x = a.rule.toLowerCase();
4424        var y = b.rule.toLowerCase();
4425        return x < y ? -1 : x > y ? 1 : 0;
4426      } else {
4427        return parseFloat(a.rule) - parseFloat(b.rule);
4428      }
4429    });
4430
4431    if (Array.isArray(markers) && markers.length > 0) {
4432      markers.forEach(function (entry) {
4433        if (typeof entry[data.heatMapMarkers.source] === 'undefined') {
4434          return;
4435        }
4436
4437        // default to correct value reference, since value will be only numeric
4438        if (data.heatMapMarkers.source === 'value') {
4439          data.heatMapMarkers.source = 'val';
4440        }
4441
4442        var val = entry[data.heatMapMarkers.source];
4443        var start;
4444
4445        if (!isNaN(val)) {
4446          val = parseFloat(val);
4447        }
4448
4449        reordered.forEach(function (ruleData) {
4450          if (isNaN(ruleData.rule)) {
4451            start = ruleData.rule.trim(); // if it's not a number
4452
4453            if (val == start) {
4454              entry.fill = ruleData.fill;
4455              entry.radius = parseFloat(ruleData.radius);
4456            }
4457          } else {
4458            start = parseFloat(ruleData.rule); // if it's a number
4459
4460            if (val >= start) {
4461              entry.fill = ruleData.fill;
4462              entry.radius = parseFloat(ruleData.radius);
4463            }
4464          }
4465        });
4466      });
4467    }
4468  } else if (seriesType === 'MapPolygonSeries') {
4469    if (!Array.isArray(data.heatMapRegions.range) || data.heatMapRegions.range.length === 0) {
4470      return;
4471    } // reorder ranges by value
4472
4473    reordered = data.heatMapRegions.range.slice(0);
4474    reordered.sort(function (a, b) {
4475      if (isNaN(a.rule)) {
4476        var x = a.rule.toLowerCase();
4477        var y = b.rule.toLowerCase();
4478        return x < y ? -1 : x > y ? 1 : 0;
4479      } else {
4480        return parseFloat(a.rule) - parseFloat(b.rule);
4481      }
4482    });
4483
4484    if (Array.isArray(regions) && regions.length > 0) {
4485      if (!isNaN(data.heatMapRegions.source)) {
4486        data.heatMapRegions.source = parseInt(data.heatMapRegions.source);
4487      }
4488
4489      regions.forEach(function (entry) {
4490        if (typeof entry[data.heatMapRegions.source] === 'undefined') {
4491          return;
4492        }
4493
4494        if (data.heatMapRegions.source === 'value') {
4495          data.heatMapRegions.source = 'val';
4496        }
4497
4498        var val = entry[data.heatMapRegions.source];
4499        var start;
4500
4501        if (!isNaN(val)) {
4502          val = parseFloat(val);
4503        }
4504
4505        reordered.forEach(function (ruleData) {
4506          if (isNaN(ruleData.rule)) {
4507            start = ruleData.rule.trim(); // if it's a number
4508
4509            if (val == start) {
4510              entry.fill = ruleData.fill;
4511            }
4512          } else {
4513            start = parseFloat(ruleData.rule); // if it's a number
4514
4515            if (val >= start) {
4516              entry.fill = ruleData.fill;
4517            }
4518          }
4519        });
4520      });
4521    }
4522  }
4523};
4524/**
4525 *
4526 * @param {object} series
4527 * @param {int} id - parent map id
4528 * @param {object} data - current map data
4529 */
4530
4531iMapsManager.setupHeatMap = function (series, id, data) {
4532  var im = this,
4533    map = im.maps[id].map,
4534    heatLegend,
4535    legendLabel,
4536    legendContainer,
4537    minRange,
4538    maxRange,
4539    target,
4540    minProp,
4541    maxProp,
4542    propTargets,
4543    dataSource,
4544    seriesType = im.getTargetSeriesType(series); // setup target
4545
4546  if (seriesType === 'MapImageSeries') {
4547    if (typeof data.heatMapMarkers.type !== 'undefined' && data.heatMapMarkers.type === 'range') {
4548      im.setupRangeHeatMap(series, data.id, data);
4549      return;
4550    }
4551
4552    target = series.mapImages.template.children.values[0];
4553    propTargets = ['fill', 'radius'];
4554    dataSource = data.heatMapMarkers;
4555  } else if (seriesType === 'MapPolygonSeries') {
4556    if (typeof data.heatMapRegions.type !== 'undefined' && data.heatMapRegions.type === 'range') {
4557      im.setupRangeHeatMap(series, data.id, data);
4558      return;
4559    }
4560
4561    target = series.mapPolygons.template;
4562    propTargets = ['fill'];
4563    dataSource = data.heatMapRegions;
4564  } else {
4565    return;
4566  }
4567
4568  // make sure we read data form the correct property
4569  series.dataFields.value = dataSource.source;
4570
4571  if (!Array.isArray(propTargets)) {
4572    propTargets = [propTargets];
4573  }
4574
4575  propTargets.map(function (prop) {
4576    // setup min/max sources
4577    if (prop === 'fill') {
4578      minProp = dataSource.minColor;
4579      maxProp = dataSource.maxColor;
4580    } else if (prop === 'radius') {
4581      minProp = dataSource.minRadius;
4582      maxProp = dataSource.maxRadius;
4583    }
4584
4585    series.heatRules.push({
4586      property: prop,
4587      target: target,
4588      min: minProp,
4589      max: maxProp,
4590    });
4591  });
4592
4593  if (im.bool(dataSource.legend)) {
4594    legendContainer = map.createChild(am4core.Container);
4595    legendContainer.align =
4596      typeof dataSource.legendAlign !== 'undefined' ? dataSource.legendAlign : 'right';
4597    legendContainer.valign =
4598      typeof dataSource.legendValign !== 'undefined' ? dataSource.legendValign : 'bottom';
4599    legendContainer.userClassName = 'mapLegendContainer';
4600    legendContainer.marginRight = am4core.percent(4);
4601    //legendContainer.parent = map;
4602
4603    if (window.innerWidth <= 780) {
4604      legendContainer.width = am4core.percent(40);
4605    } else {
4606      legendContainer.width = am4core.percent(25);
4607    }
4608
4609    // legend title
4610    if (typeof dataSource.label !== 'undefined' && dataSource.label !== '') {
4611      legendLabel = legendContainer.createChild(am4core.Label);
4612      legendLabel.text = dataSource.label;
4613      legendLabel.horizontalCenter = 'left';
4614      legendLabel.verticalCenter = 'top';
4615
4616      legendLabel.fontSize = 12;
4617      legendLabel.paddingBottom = 40;
4618
4619      if (window.innerWidth <= 780) {
4620        legendLabel.fontSize = 10;
4621        legendLabel.paddingBottom = 48;
4622      } else {
4623        legendLabel.fontSize = 12;
4624        legendLabel.paddingBottom = 48;
4625      }
4626
4627      legendLabel.nonScaling = false;
4628      legendLabel.clickable = false;
4629      legendLabel.focusable = false;
4630      legendLabel.hoverable = false;
4631    }
4632
4633    //legendLabel.parent = heatLegend;
4634    //label.insertAfter(heatLegend.valueAxis);
4635    //heatLegend.valueAxis.title.text = 'test title';
4636
4637    heatLegend = legendContainer.createChild(am4maps.HeatLegend);
4638    heatLegend.series = series;
4639    heatLegend.align = 'right';
4640    heatLegend.valign = 'bottom';
4641    heatLegend.width = am4core.percent(100);
4642    //heatLegend.marginRight = am4core.percent(4);
4643    heatLegend.minValue = 0;
4644    heatLegend.maxValue = 99999999999999; // Set up custom heat map legend labels using axis ranges
4645
4646    minRange = heatLegend.valueAxis.axisRanges.create();
4647    minRange.value = heatLegend.minValue;
4648    minRange.label.text = dataSource.minLabel;
4649    maxRange = heatLegend.valueAxis.axisRanges.create();
4650    maxRange.value = heatLegend.maxValue;
4651    maxRange.label.text = dataSource.maxLabel;
4652
4653    minRange.label.fontSize = 12;
4654    maxRange.label.fontSize = 12;
4655
4656    if (window.innerWidth <= 780) {
4657      minRange.label.fontSize = 10;
4658      maxRange.label.fontSize = 10;
4659    } else {
4660      minRange.label.fontSize = 12;
4661      maxRange.label.fontSize = 12;
4662    }
4663
4664    // Blank out internal heat legend value axis labels
4665    heatLegend.valueAxis.renderer.labels.template.adapter.add('text', function () {
4666      return '';
4667    });
4668  }
4669};
4670
4671iMapsManager.drillTo = function (id, ev, currentRegion, customMap) {
4672  var im = iMapsManager,
4673    data = im.maps[id].data,
4674    allCurrentSeries = iMapsManager.maps[id].series,
4675    baseSeries = iMapsManager.maps[id].baseSeries,
4676    allBaseSeries = iMapsManager.maps[id].allBaseSeries,
4677    bgSeries = im.maps[id].backgroundSeries,
4678    opacity,
4679    i,
4680    len;
4681
4682  (opacity = typeof igmDrilldownBaseMapOpacity !== 'undefined' ? igmDrilldownBaseMapOpacity : 0.3),
4683    // hide all others except this one and baseSeries
4684    // if were opening a custom map
4685    (customMap = customMap || false);
4686
4687  // hide or fade series
4688  for (i = 0, len = allCurrentSeries.length; i < len; i++) {
4689    if (baseSeries.includes(allCurrentSeries[i])) {
4690      // let's check if new option to keep base map is enabled
4691      if (typeof data.alwaysKeepBase === 'undefined' || !im.bool(data.alwaysKeepBase)) {
4692        allCurrentSeries[i].opacity = opacity;
4693
4694        // hide image background
4695        bgSeries.opacity = opacity;
4696      }
4697    } else if (allBaseSeries.includes(allCurrentSeries[i])) {
4698      // let's check if new option to keep base map is enabled
4699      if (typeof data.alwaysKeepBase === 'undefined' || !im.bool(data.alwaysKeepBase)) {
4700        allCurrentSeries[i].hide();
4701      }
4702    } else {
4703      allCurrentSeries[i].hide();
4704    }
4705  }
4706
4707  for (i = 0, len = currentRegion.length; i < len; i++) {
4708    currentRegion[i].show();
4709  }
4710  // is drilling
4711  iMapsManager.maps[id].isDrilling = true;
4712
4713  // zoom to region - it will only work when zoom is enabled so that controls exist
4714  if (!ev.zooming) {
4715    if (customMap) {
4716      console.log('drill to specific map');
4717      im.zoomToMap(ev, currentRegion);
4718      return;
4719    }
4720
4721    im.zoomToRegion(ev, id);
4722  }
4723};
4724
4725iMapsManager.drillDown = function (id, ev) {
4726  var mapName = iMapsRouter.iso2cleanName(ev.target.dataItem.dataContext.id, id),
4727    targetID = ev.target.dataItem.dataContext.id,
4728    allCurrentSeries = iMapsManager.maps[id].series,
4729    clicked = ev.target.dataItem.dataContext,
4730    currentRegion,
4731    customMap = false,
4732    baseSeries = iMapsManager.maps[id].baseSeries,
4733    allBaseSeries = iMapsManager.maps[id].allBaseSeries,
4734    i,
4735    len,
4736    checkSeries = [],
4737    seriesExists = false;
4738  console.log('Map Name:', mapName);
4739  console.log('Available Series:', iMapsManager.maps[id].seriesIndex);
4740  console.log('Available Series by ID: ', iMapsManager.maps[id].seriesById); // check if geofile info exists or return if the id is numeric
4741
4742  if (!mapName || !isNaN(targetID)) {
4743    // well, let's only return if it's not showing another map
4744    // code needs better logic...
4745    if (_typeof(clicked) === undefined || clicked.action !== 'igm_display_map') {
4746      return false;
4747    }
4748  }
4749
4750  if (ev.target.polygon) {
4751    // what we need to check
4752    checkSeries.push(mapName); // check if series exists for this map
4753
4754    checkSeries.forEach(function (ser) {
4755      if (_typeof(clicked) !== undefined && clicked.action == 'igm_display_map') {
4756        console.log('Display custom map ' + clicked.content);
4757
4758        if (Array.isArray(iMapsManager.maps[id].seriesById[parseInt(clicked.content)])) {
4759          seriesExists = true;
4760          currentRegion = iMapsManager.maps[id].seriesById[parseInt(clicked.content)];
4761          customMap = true;
4762        }
4763      } else {
4764        if (Array.isArray(iMapsManager.maps[id].seriesIndex[ser])) {
4765          seriesExists = true;
4766          currentRegion = iMapsManager.maps[id].seriesIndex[ser];
4767        }
4768      }
4769    });
4770
4771    if (seriesExists) {
4772      iMapsManager.drillTo(id, ev, currentRegion, customMap);
4773      iMapsManager.maps[id].drilledTo = currentRegion[0].mapID;
4774    } else {
4775      // if the target is part of current series, do nothing
4776      if (currentRegion === ev.target.series) {
4777        iMapsManager.maps[id].isDrilling = false;
4778        return;
4779      }
4780      // if target is base series, show it
4781      if (baseSeries.includes(ev.target.series)) {
4782        iMapsManager.maps[id].isDrilling = false;
4783        iMapsManager.maps[id].drilledTo = false;
4784
4785        // hide all except baseSeries
4786        for (i = 0, len = allCurrentSeries.length; i < len; i++) {
4787          if (allBaseSeries.includes(allCurrentSeries[i])) {
4788            allCurrentSeries[i].show();
4789          } else {
4790            allCurrentSeries[i].hide();
4791          }
4792        }
4793      }
4794    }
4795  }
4796};
4797/*HELPERS*/
4798
4799iMapsManager.getSelected = function (id) {
4800  var im = this,
4801    map = im.maps[id],
4802    selected = map.selected || false,
4803    multiple = [];
4804
4805  if (selected) {
4806    if (Array.isArray(selected)) {
4807      selected.forEach(function (sel) {
4808        multiple.push(sel.dataItem.dataContext);
4809      });
4810      return multiple;
4811    }
4812
4813    return selected.dataItem.dataContext;
4814  } else {
4815    return false;
4816  }
4817};
4818
4819iMapsManager.getHovered = function (id) {
4820  var im = this,
4821    map = im.maps[id],
4822    hovered = map.hovered || false,
4823    multiple = [];
4824
4825  if (hovered) {
4826    if (Array.isArray(hovered)) {
4827      hovered.forEach(function (sel) {
4828        multiple.push(sel.dataItem.dataContext);
4829      });
4830      return multiple;
4831    }
4832  } else {
4833    return false;
4834  }
4835};
4836
4837iMapsManager.getHighlighted = function (id) {
4838  var im = this,
4839    map = im.maps[id],
4840    highlighted = map.highlighted || false,
4841    multiple = [];
4842
4843  if (highlighted) {
4844    if (Array.isArray(highlighted)) {
4845      highlighted.forEach(function (sel) {
4846        multiple.push(sel.dataItem.dataContext);
4847      });
4848      return multiple;
4849    }
4850
4851    return highlighted.dataItem.dataContext;
4852  } else {
4853    return false;
4854  }
4855};
4856
4857iMapsManager.clearSelected = function (id, keepThis, skipReset) {
4858  var im = this,
4859    map = im.maps[id],
4860    selected = map.selected || [];
4861
4862  // to keep the state of this element
4863  keepThis = keepThis || false;
4864  skipReset = skipReset || false;
4865
4866  if (Array.isArray(selected) && selected.length > 0) {
4867    selected.forEach(function (polygon) {
4868      if (
4869        polygon !== keepThis &&
4870        typeof polygon === 'object' &&
4871        typeof polygon.isHover !== 'undefined'
4872      ) {
4873        polygon.isHover = false;
4874        polygon.isActive = false;
4875        polygon.isGroupActive = false;
4876
4877        // there were some issues on mobile tap (singleHit) where the tooltip would disappear,
4878        // so we added this check before hiding the tooltip
4879        if (!keepThis) {
4880          polygon.hideTooltip(0); // needed to hide tooltip
4881        }
4882
4883        if (polygon.selectedFromGroup) {
4884          polygon.hideTooltip(0); // needed to hide tooltip when group was selected
4885          polygon.selectedFromGroup = false;
4886        }
4887
4888        polygon.setState('default');
4889      }
4890    });
4891    selected = [];
4892  }
4893
4894  if (!keepThis && typeof iMapsActions !== 'undefined') {
4895    if (!skipReset) {
4896      //iMapsActions.resetActions(id);
4897    }
4898    map.selected = [];
4899  } else {
4900    map.selected = [keepThis];
4901  }
4902
4903  return map.selected;
4904};
4905
4906iMapsManager.clearHighlighted = function (id) {
4907  var im = this,
4908    map = im.maps[id],
4909    highlighted = map.highlighted || [];
4910
4911  if (Array.isArray(highlighted) && highlighted.length > 0) {
4912    highlighted.forEach(function (polygon) {
4913      polygon.isHover = false;
4914      polygon.isActive = false;
4915      polygon.setState('default');
4916    });
4917    highlighted = [];
4918  }
4919
4920  return highlighted;
4921};
4922/*
4923 * setup hover events
4924 * id - id of the map
4925 * eID - hovered element ID
4926 * forcedFixedTooltip - if we should force the tooltip to be fixed or not
4927 */
4928
4929iMapsManager.hover = function (id, eID, forceFixedTooltip) {
4930  var im = this,
4931    map = im.maps[id],
4932    series = map.series,
4933    hovered = map.hovered || [],
4934    hover,
4935    group,
4936    defaultShowTooltipOn,
4937    defaultTooltipPosition; // map sure it's string
4938
4939  if (Number.isInteger(eID)) {
4940    eID = eID.toString();
4941  } // Force fixed position?
4942
4943  if (typeof forceFixedTooltip === 'undefined') {
4944    forceFixedTooltip = true;
4945  }
4946
4947  iMapsManager.clearHovered(id);
4948  hovered = [];
4949
4950  if (Array.isArray(series)) {
4951    for (var i = 0, len = series.length; i < len; i++) {
4952      // regionSeries
4953      if (series[i].mapPolygons) {
4954        // multiple
4955        // check if group
4956        if (eID.includes(',')) {
4957          // foreach code
4958          group = eID.split(',');
4959          group.forEach(function (rxid) {
4960            // single
4961            hover = series[i].getPolygonById(rxid.trim());
4962
4963            if (hover) {
4964              if (forceFixedTooltip) {
4965                defaultTooltipPosition =
4966                  typeof hover.tooltipPosition !== 'undefined' ? hover.tooltipPosition : 'fixed';
4967                defaultShowTooltipOn =
4968                  typeof hover.showTooltipOn !== 'undefined' ? hover.showTooltipOn : 'hover';
4969                hover.tooltipPosition = 'fixed';
4970                hover.showTooltipOn = 'always';
4971                hovered.push(hover);
4972                hover.dispatchImmediately('over');
4973                hover.isHover = true;
4974                hover.tooltipPosition = defaultTooltipPosition;
4975                hover.showTooltipOn = defaultShowTooltipOn;
4976              } else {
4977                hovered.push(hover);
4978                hover.dispatchImmediately('over');
4979                hover.isHover = true;
4980              }
4981            }
4982          });
4983        } else {
4984          // single region
4985          hover = series[i].getPolygonById(eID);
4986
4987          if (hover) {
4988            if (forceFixedTooltip) {
4989              defaultTooltipPosition = hover.tooltipPosition;
4990              hover.tooltipPosition = 'fixed';
4991              hovered = [hover];
4992              hover.dispatchImmediately('over');
4993              hover.isHover = true;
4994              hover.tooltipPosition = defaultTooltipPosition;
4995            } else {
4996              hovered = [hover];
4997              hover.dispatchImmediately('over');
4998              hover.isHover = true;
4999            }
5000          }
5001        }
5002      }
5003      // imageSeries
5004      if (series[i].mapImages) {
5005        // multiple markers
5006        if (eID.includes(',')) {
5007          // foreach code
5008          group = eID.split(',');
5009          group.forEach(function (rxid) {
5010            hover = series[i].getImageById(rxid);
5011
5012            if (hover) {
5013              if (forceFixedTooltip) {
5014                defaultTooltipPosition = hover.tooltipPosition;
5015                defaultShowTooltipOn = hover.showTooltipOn;
5016                hover.tooltipPosition = 'fixed';
5017                hover.showTooltipOn = 'always';
5018                hovered = [hover];
5019                hover.dispatchImmediately('over');
5020                hover.isHover = true;
5021                hover.setState('hover');
5022                hover.children.each(function (child) {
5023                  if (child.className === 'Circle') {
5024                    child.showTooltip(0);
5025                  }
5026                });
5027                hover.tooltipPosition = defaultTooltipPosition;
5028                hover.showTooltipOn = defaultShowTooltipOn;
5029              } else {
5030                hovered = [hover];
5031                hover.dispatchImmediately('over');
5032                hover.isHover = true;
5033                hover.setState('hover');
5034              }
5035            }
5036          });
5037        }
5038        // single marker
5039        else {
5040          hover = series[i].getImageById(eID);
5041
5042          if (hover) {
5043            if (forceFixedTooltip) {
5044              defaultTooltipPosition = hover.tooltipPosition;
5045              hover.tooltipPosition = 'fixed';
5046              hovered = [hover];
5047              hover.dispatchImmediately('over');
5048              hover.isHover = true;
5049              hover.setState('hover');
5050              hover.children.each(function (child) {
5051                if (child.className === 'Circle') {
5052                  child.showTooltip(0);
5053                }
5054              });
5055              hover.tooltipPosition = defaultTooltipPosition;
5056            } else {
5057              hovered = [hover];
5058              hover.dispatchImmediately('over');
5059              hover.isHover = true;
5060              hover.setState('hover');
5061            }
5062          }
5063        }
5064      }
5065    }
5066  }
5067
5068  map.hovered = hovered;
5069  return hover;
5070};
5071
5072iMapsManager.clearHovered = function (id, eID) {
5073  var im = this,
5074    map = im.maps[id],
5075    hovered = map.hovered || false,
5076    series = map.series,
5077    hover;
5078  eID = eID || false;
5079
5080  if (eID) {
5081    if (Array.isArray(series)) {
5082      for (var i = 0, len = series.length; i < len; i++) {
5083        // regionSeries - specific region
5084        if (series[i].mapPolygons) {
5085          hover = series[i].getPolygonById(eID);
5086
5087          if (hover) {
5088            hover.dispatchImmediately('out');
5089            hover.isHover = false;
5090          }
5091        }
5092
5093        // imageSeries - specific marker
5094        if (series[i].mapImages) {
5095          hover = series[i].getImageById(eID);
5096
5097          if (hover) {
5098            hover.isHover = false;
5099            hover.setState('default');
5100            hover.dispatchImmediately('out');
5101          }
5102        }
5103      }
5104    }
5105  }
5106  // all of the regions or markers
5107  else {
5108    if (hovered) {
5109      hovered.forEach(function (hov) {
5110        hov.dispatchImmediately('out');
5111        if (typeof hov.isGroupActive === 'undefined' || !hov.isGroupActive) {
5112          hov.setState('default');
5113          hov.isHover = false;
5114        }
5115        if (typeof hov.children !== 'undefined') {
5116          hov.children.each(function (child) {
5117            if (child.className === 'Circle') {
5118              child.hideTooltip(0);
5119            }
5120          });
5121        }
5122      });
5123      map.hovered = [];
5124      return true;
5125    }
5126  }
5127
5128  return false;
5129};
5130/**
5131 * id - map ID
5132 * elID - element to highlight
5133 */
5134
5135iMapsManager.highlight = function (id, elID) {
5136  var im = this,
5137    map = im.maps[id],
5138    series = map.series,
5139    select,
5140    highlighted = map.highlighted || [],
5141    group; // map sure it's string
5142
5143  if (Number.isInteger(elID)) {
5144    elID = elID.toString();
5145  } //iMapsManager.clearSelected(id);
5146
5147  if (Array.isArray(series)) {
5148    for (var i = 0, len = series.length; i < len; i++) {
5149      // regionSeries
5150      if (series[i].mapPolygons) {
5151        // check if group
5152        if (elID.includes(',')) {
5153          group = elID.split(',');
5154          group.forEach(function (rxid) {
5155            select = series[i].getPolygonById(rxid.trim());
5156
5157            if (typeof select !== 'undefined' && select) {
5158              if (select.dataItem.dataContext.madeFromGeoData) {
5159                return;
5160              }
5161
5162              select.setState('highlight');
5163              highlighted.push(select);
5164            }
5165          });
5166        } else {
5167          select = series[i].getPolygonById(elID);
5168
5169          if (typeof select !== 'undefined' && select) {
5170            select.setState('highlight');
5171            highlighted.push(select);
5172          }
5173        }
5174      } // imageSeries
5175
5176      if (series[i].mapImages) {
5177        if (elID.includes(',')) {
5178          group = elID.split(',');
5179          group.forEach(function (rxid) {
5180            select = series[i].getImageById(rxid);
5181
5182            if (typeof select !== 'undefined' && select) {
5183              select.setStateOnChildren = true;
5184              select.setState('highlight');
5185              highlighted.push(select);
5186            }
5187          });
5188        } else {
5189          select = series[i].getImageById(elID);
5190
5191          if (typeof select !== 'undefined' && select) {
5192            select.setStateOnChildren = true;
5193            select.setState('highlight');
5194            highlighted.push(select);
5195          }
5196        }
5197      }
5198    }
5199  }
5200
5201  map.highlighted = highlighted;
5202  return select;
5203};
5204
5205iMapsManager.getTargetSeriesType = function (el) {
5206  var className = el.className;
5207  return className;
5208};
5209/**
5210 * Setups clustered series based on coordinate values from data
5211 */
5212
5213iMapsManager.setupClusters = function (data, id, overlay) {
5214  var im = this,
5215    map = im.maps[id],
5216    series = [],
5217    markerSeries,
5218    tempData = {},
5219    biasLevels = [],
5220    zoomLevels = [],
5221    biasSteps = 4,
5222    i = 0,
5223    prevBias = parseFloat(data.clusterMarkers.maxBias),
5224    maxZoomLevel = parseFloat(data.clusterMarkers.zoomLevel) || 20,
5225    tooltipTemplate =
5226      typeof data.clusterMarkers.tooltipTemplate !== 'undefined'
5227        ? data.clusterMarkers.tooltipTemplate
5228        : false;
5229  overlay = overlay || false;
5230  var currentMap = overlay ? overlay : id;
5231
5232  while (i <= biasSteps) {
5233    biasLevels.push(prevBias);
5234    prevBias = prevBias / 2;
5235    zoomLevels.push(maxZoomLevel);
5236
5237    // make sure the last one is set to 1, otherwise it will be hidden
5238    maxZoomLevel = i == 3 ? 1 : Math.ceil(maxZoomLevel / 2);
5239    i++;
5240  }
5241
5242  // reverse array to match detail level
5243  zoomLevels.reverse().pop();
5244  biasLevels.pop();
5245
5246  if (typeof map.clusterSeries[currentMap] === 'undefined') {
5247    map.clusterSeries[currentMap] = {
5248      zoomLevels: {},
5249      overlay: overlay,
5250    };
5251  }
5252
5253  if (Array.isArray(data.roundMarkers)) {
5254    biasLevels.forEach(function (item, index) {
5255      series = geocluster(data.roundMarkers, item, data.markerDefaults, tooltipTemplate);
5256      tempData = Object.assign({}, data);
5257      tempData.roundMarkers = series;
5258      markerSeries = im.pushRoundMarkerSeries(id, tempData);
5259      markerSeries.name = tempData.title || 'Map';
5260      markerSeries.hiddenInLegend = true; // set as cluster to be identified later in select method
5261
5262      markerSeries.isCluster = true;
5263      map.clusterSeries[currentMap].zoomLevels[zoomLevels[index]] = markerSeries;
5264
5265      if (index === 0 && !overlay) {
5266        // this somehow interferes with the clusters after home button is clicked
5267        // im.maps[id].allBaseSeries.push(markerSeries);
5268      }
5269
5270      // hide all series except the first
5271      //if (index > 0 ) {
5272      markerSeries.hidden = true;
5273      //}
5274    });
5275  }
5276
5277  return true;
5278};
5279
5280/**
5281 * triggers when map is ready
5282 * @param {int} id of the map
5283 */
5284iMapsManager.triggerOnReady = function (id, data) {
5285  let isCustom = data.map == 'custom' ? true : false;
5286  let urlParams = new URLSearchParams(window.location.search);
5287  let myParam = urlParams.get('mregion');
5288  if (myParam) {
5289    //if it's a custom map, do nothing, we will trigger it on the map appeared event
5290    if (!isCustom) {
5291      iMapsManager.select(id, myParam, true, true);
5292    }
5293  }
5294};
5295
5296/**
5297 * triggers when map is ready
5298 * @param {int} id of the map
5299 */
5300iMapsManager.triggerOnAppeared = function (id, data) {
5301  let isCustom = data.map == 'custom' ? true : false;
5302  let urlParams = new URLSearchParams(window.location.search);
5303  let myParam = urlParams.get('mregion');
5304  if (myParam) {
5305    //if it's a custom map, do nothing, we will trigger it on the map appeared event
5306    if (isCustom) {
5307      setTimeout(function () {
5308        iMapsManager.select(id, myParam, true, true);
5309      }, 500);
5310    }
5311  }
5312};
5313
5314/**
5315 * Adds a new region Series currently not loaded, adding the script to the body of the page and creating a new series after
5316 * id - map id to attach series
5317 * dataContent - object with data that would typically be a polygon dataContent, like name and id
5318 * config - config object for the new series being added
5319 *
5320 * @return newSeries - the new created series object
5321 */
5322
5323iMapsManager.addGeoFileSeries = function (id, dataContext) {
5324  var newSeries,
5325    geoFiles = iMapsRouter.getGeoFiles(dataContext);
5326  var scriptPromise = new Promise(function (resolve, reject) {
5327    var script = document.createElement('script');
5328    document.body.appendChild(script);
5329    script.onload = resolve;
5330    script.onerror = reject;
5331    script.async = true;
5332    script.src = geoFiles.src;
5333  });
5334  scriptPromise.then(function () {
5335    var data = {
5336      title: geoFiles.title,
5337      map: geoFiles.map,
5338      regions: [],
5339      config: data, // not working, we changed the config to be at parent level with data
5340    };
5341    iMapsManager.maps[id].seriesIndex[geoFiles.map] = [];
5342    newSeries = iMapsManager.pushRegionSeries(id, data);
5343    iMapsManager.maps[id].seriesIndex[geoFiles.map].push(newSeries);
5344    return newSeries;
5345  });
5346  return false;
5347};
5348
5349iMapsManager.handleInfoBox = function (id) {
5350  var im = this,
5351    map = im.maps[id].map,
5352    events = ['ready', 'mappositionchanged', 'zoomlevelchanged'],
5353    container = document.getElementById('map_visual_info'),
5354    coordinatesc = document.getElementById('map_click_events_coordinates');
5355
5356  if (container) {
5357    iMapsManager.populateInfo(id, container); // zoom, etc
5358
5359    events.forEach(function (event) {
5360      map.events.on(
5361        event,
5362        function () {
5363          iMapsManager.populateInfo(id, container);
5364        },
5365        this
5366      );
5367    });
5368  }
5369
5370  if (coordinatesc) {
5371    map.events.on(
5372      'hit',
5373      function (ev) {
5374        var coordinates = map.svgPointToGeo(ev.svgPoint);
5375        var lat = Number(coordinates.latitude).toFixed(6);
5376
5377        var _long = Number(coordinates.longitude).toFixed(6); // latitude
5378
5379        var latEl = document.createElement('div');
5380        var latLabelEl = document.createElement('span');
5381        var latValueEl = document.createElement('span');
5382        latLabelEl.classList.add('info--unselectable');
5383        latValueEl.classList.add('map_clicked_lat');
5384        latLabelEl.innerHTML = 'LAT: ';
5385        latValueEl.innerHTML = lat;
5386        latEl.appendChild(latLabelEl);
5387        latEl.appendChild(latValueEl); // longitude
5388
5389        var longEl = document.createElement('div');
5390        var longLabelEl = document.createElement('span');
5391        var longValueEl = document.createElement('span');
5392        longLabelEl.classList.add('info--unselectable');
5393        longValueEl.classList.add('map_clicked_long');
5394        longLabelEl.innerHTML = 'LON: ';
5395        longValueEl.innerHTML = _long;
5396        longEl.appendChild(longLabelEl);
5397        longEl.appendChild(longValueEl);
5398        coordinatesc.innerHTML = '';
5399        coordinatesc.appendChild(latEl);
5400        coordinatesc.appendChild(longEl);
5401        coordinatesc.parentElement.style.display = 'block';
5402        var event = new CustomEvent('mapPointClicked', {
5403          detail: {
5404            latitude: lat,
5405            longitude: _long,
5406          },
5407        });
5408        document.dispatchEvent(event);
5409      },
5410      this
5411    );
5412  }
5413};
5414
5415iMapsManager.populateInfo = function (id, container) {
5416  var im = this,
5417    map = im.maps[id].map,
5418    info = '';
5419  info += 'Zoom Level: ' + parseFloat(Number(map.zoomLevel).toFixed(2)) + '<br>';
5420  info +=
5421    'Center Coordinates: <br>' +
5422    "<span class='info--unselectable'>LAT </span><span>" +
5423    Number(map.zoomGeoPoint.latitude).toFixed(6) +
5424    '</span>';
5425  info +=
5426    "<br><span class='info--unselectable'>LONG </span><span>" +
5427    Number(map.zoomGeoPoint.longitude).toFixed(6) +
5428    '</span><br>';
5429  container.innerHTML = info;
5430
5431  let centerData = {
5432    zoom: parseFloat(Number(map.zoomLevel).toFixed(2)),
5433    lat: Number(map.zoomGeoPoint.latitude).toFixed(6),
5434    long: Number(map.zoomGeoPoint.longitude).toFixed(6),
5435  };
5436
5437  container.setAttribute('data-visual', JSON.stringify(centerData));
5438};
5439
5440iMapsManager.hideAllSeries = function (id, keepBase) {
5441  id = parseInt(id);
5442
5443  if (!id) {
5444    return;
5445  }
5446
5447  keepBase = keepBase || false;
5448  var map = iMaps.maps[id];
5449  var groupedSeries = map.groupedBaseRegionSeries;
5450  var allbaseSeries = map.allBaseSeries;
5451
5452  for (var index = 0; index < map.series.length; index++) {
5453    var serie = map.series[index];
5454
5455    //if (baseRegionSeries !== serie && !groupedSeries.includes(serie)) {
5456    // we don't need to check if it's not the base series at this point
5457
5458    if (!groupedSeries.includes(serie)) {
5459      // in case we need to check also if we keep base series
5460      if (!keepBase || (keepBase && !allbaseSeries.includes(serie))) {
5461        serie.hide();
5462      }
5463    }
5464  }
5465};
5466
5467iMapsManager.showAllSeries = function (id) {
5468  id = parseInt(id);
5469
5470  if (!id) {
5471    return;
5472  }
5473
5474  var map = iMaps.maps[id];
5475
5476  for (var index = 0; index < map.series.length; index++) {
5477    var serie = map.series[index];
5478
5479    if (typeof serie.isCluster === 'undefined' || serie.isCluster === false) {
5480      serie.show();
5481    }
5482  }
5483};
5484/** Util function to return boolean value of string */
5485
5486iMapsManager.bool = function (string) {
5487  var bool =
5488    Number(string) === 0 || string === 'false' || typeof string === 'undefined' ? false : true;
5489  return bool;
5490};
5491
5492iMapsManager.isJSON = function (str) {
5493  try {
5494    JSON.parse(str);
5495  } catch {
5496    return false;
5497  }
5498
5499  return true;
5500};
5501/* Closest Polyfill */
5502
5503if (!Element.prototype.matches) {
5504  Element.prototype.matches =
5505    Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
5506}
5507
5508if (!Element.prototype.closest) {
5509  Element.prototype.closest = function (s) {
5510    var el = this;
5511
5512    do {
5513      if (el.matches(s)) {
5514        return el;
5515      }
5516      el = el.parentElement || el.parentNode;
5517    } while (el !== null && el.nodeType === 1);
5518
5519    return null;
5520  };
5521}
5522/** Library for elements
5523 */
5524
5525iMapsManager.library = {
5526  icons: {
5527    goFullIconPath:
5528      'm15.78742,5.93715l-3.95414,3.95414l3.95414,3.95414l1.60393,-1.60393q0.32301,-0.34529 0.77969,-0.15594q0.4344,0.18935 0.4344,0.65717l0,4.99002q0,0.2896 -0.21163,0.50123t-0.50123,0.21163l-4.99002,0q-0.46781,0 -0.65717,-0.44554q-0.18935,-0.4344 0.15594,-0.76855l1.60393,-1.60393l-3.95414,-3.95414l-3.95414,3.95414l1.60393,1.60393q0.34529,0.33415 0.15594,0.76855q-0.18935,0.44554 -0.65717,0.44554l-4.99002,0q-0.2896,0 -0.50123,-0.21163t-0.21163,-0.50123l0,-4.99002q0,-0.46781 0.44554,-0.65717q0.4344,-0.18935 0.76855,0.15594l1.60393,1.60393l3.95414,-3.95414l-3.95414,-3.95414l-1.60393,1.60393q-0.21163,0.21163 -0.50123,0.21163q-0.13366,0 -0.26732,-0.05569q-0.44554,-0.18935 -0.44554,-0.65717l0,-4.99002q0,-0.2896 0.21163,-0.50123t0.50123,-0.21163l4.99002,0q0.46781,0 0.65717,0.44554q0.18935,0.4344 -0.15594,0.76855l-1.60393,1.60393l3.95414,3.95414l3.95414,-3.95414l-1.60393,-1.60393q-0.34529,-0.33415 -0.15594,-0.76855q0.18935,-0.44554 0.65717,-0.44554l4.99002,0q0.2896,0 0.50123,0.21163t0.21163,0.50123l0,4.99002q0,0.46781 -0.4344,0.65717q-0.1448,0.05569 -0.27846,0.05569q-0.2896,0 -0.50123,-0.21163l-1.60393,-1.60393z',
5529    exitFullIconPath:
5530      'm10.04411,10.81638l0,5.40556q0,0.31372 -0.22925,0.54297t-0.54297,0.22925t-0.54297,-0.22925l-1.7375,-1.7375l-4.00591,4.00591q-0.12066,0.12066 -0.27752,0.12066t-0.27752,-0.12066l-1.37552,-1.37552q-0.12066,-0.12066 -0.12066,-0.27752t0.12066,-0.27752l4.00591,-4.00591l-1.7375,-1.7375q-0.22925,-0.22925 -0.22925,-0.54297t0.22925,-0.54297t0.54297,-0.22925l5.40556,0q0.31372,0 0.54297,0.22925t0.22925,0.54297zm9.10982,-8.10834q0,0.15686 -0.12066,0.27752l-4.00591,4.00591l1.7375,1.7375q0.22925,0.22925 0.22925,0.54297t-0.22925,0.54297t-0.54297,0.22925l-5.40556,0q-0.31372,0 -0.54297,-0.22925t-0.22925,-0.54297l0,-5.40556q0,-0.31372 0.22925,-0.54297t0.54297,-0.22925t0.54297,0.22925l1.7375,1.7375l4.00591,-4.00591q0.12066,-0.12066 0.27752,-0.12066t0.27752,0.12066l1.37552,1.37552q0.12066,0.12066 0.12066,0.27752z',
5531  },
5532};
5533
5534iMapsManager.handleExternalZoom = function (id) {
5535  var mapContainer, mapBox, controls, homeBtn, zoomInBtn, zoomOutBtn;
5536  mapContainer = document.getElementById('map_wrapper_' + id);
5537  var im = this,
5538    data = im.maps[id].data,
5539    allCurrentSeries = im.maps[id].series,
5540    allBaseSeries = im.maps[id].allBaseSeries;
5541
5542  if (!mapContainer) {
5543    return;
5544  }
5545
5546  mapBox = mapContainer.querySelector('.map_box');
5547  mapContainer.classList.add('map_has_external_controls'); // home button
5548
5549  homeBtn = document.createElement('div');
5550  homeBtn.setAttribute('id', 'map_home_buttom_' + id);
5551  homeBtn.setAttribute('data-map-id', id);
5552  homeBtn.classList.add('map_home_button');
5553  homeBtn.innerHTML =
5554    '<svg height="20" width="20"><path d="M16,8 L14,8 L14,16 L10,16 L10,10 L6,10 L6,16 L2,16 L2,8 L0,8 L8,0 L16,8 Z M16,8" /></svg>';
5555  homeBtn.addEventListener('click', function () {
5556    var id = this.getAttribute('data-map-id');
5557    iMaps.maps[id].map.goHome();
5558
5559    // in case drillDown is enabled, we hide everything else
5560    if (im.bool(data.drillDownOnClick)) {
5561      for (var i = 0, len = allCurrentSeries.length; i < len; i++) {
5562        allCurrentSeries[i].hide(); //map.deltaLongitude = 0;
5563      }
5564
5565      for (var ib = 0, lenb = allBaseSeries.length; ib < lenb; ib++) {
5566        // this is messing the cluster markers on base map
5567        allBaseSeries[ib].show();
5568      }
5569
5570      iMapsManager.maps[id].drilledTo = false;
5571      iMapsManager.maps[id].isDrilling = false;
5572    }
5573
5574    // reset actions
5575    if (typeof iMapsActions !== 'undefined' && typeof iMapsActions.resetActions !== 'undefined') {
5576      iMapsActions.resetActions(id);
5577    }
5578  }); // zoom in
5579
5580  zoomInBtn = document.createElement('div');
5581  zoomInBtn.setAttribute('id', 'map_zoomin_buttom_' + id);
5582  zoomInBtn.setAttribute('data-map-id', id);
5583  zoomInBtn.classList.add('map_zoomin_button');
5584  zoomInBtn.innerHTML = '+';
5585  zoomInBtn.addEventListener('click', function () {
5586    var id = this.getAttribute('data-map-id');
5587    iMaps.maps[id].map.zoomIn();
5588  }); // zoom out
5589
5590  zoomOutBtn = document.createElement('div');
5591  zoomOutBtn.setAttribute('id', 'map_zoomout_buttom_' + id);
5592  zoomOutBtn.setAttribute('data-map-id', id);
5593  zoomOutBtn.classList.add('map_zoomout_button');
5594  zoomOutBtn.innerHTML = '-';
5595  zoomOutBtn.addEventListener('click', function () {
5596    var id = this.getAttribute('data-map-id');
5597    iMaps.maps[id].map.zoomOut();
5598  }); // controls container
5599
5600  controls = document.createElement('div');
5601  controls.setAttribute('id', 'map_controls_' + id);
5602  controls.classList.add('map_controls');
5603  controls.appendChild(homeBtn);
5604  controls.appendChild(zoomInBtn);
5605  controls.appendChild(zoomOutBtn); // add buttons
5606
5607  mapBox.parentNode.insertBefore(controls, mapBox.nextSibling);
5608};
5609
5610iMapsManager.toggleFullscreen = function (element) {
5611  // se não está em tela cheia
5612  if (!document.fullscreen && !document.webkitIsFullScreen) {
5613    // solicitar tela cheia
5614    if (element.requestFullscreen) {
5615      // default
5616      element.requestFullscreen();
5617    } else if (element.mozRequestFullScreen) {
5618      // Mozilla
5619      element.mozRequestFullScreen();
5620    } else if (element.webkitRequestFullscreen) {
5621      // Chrome e Safari
5622      element.webkitRequestFullscreen();
5623    } else if (element.msRequestFullscreen) {
5624      // Internet Explorer
5625      element.msRequestFullscreen();
5626    }
5627  } else {
5628    // exit fullscreen
5629    if (document.exitFullscreen) {
5630      // default
5631      document.exitFullscreen();
5632    } else if (document.mozCancelFullScreen) {
5633      // Mozilla
5634      document.mozCancelFullScreen();
5635    } else if (document.webkitExitFullscreen) {
5636      // Chrome e Safari
5637      document.webkitExitFullscreen();
5638    } else if (document.msExitFullscreen) {
5639      // Internet Explorer
5640      document.msExitFullscreen();
5641    }
5642    iMapsManager.isFullScreen = false;
5643  }
5644};
5645
5646iMapsManager.isTouchScreenDevice = function () {
5647  return 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
5648};
5649
5650/**
5651 * This function is same as PHP's nl2br() with default parameters.
5652 *
5653 * @param {string} str Input text
5654 * @param {boolean} replaceMode Use replace instead of insert
5655 * @param {boolean} isXhtml Use XHTML
5656 * @return {string} Filtered text
5657 */
5658
5659iMapsManager.nl2br = function (str) {
5660  var replaceStr = 'HI<br>HI';
5661  return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, replaceStr);
5662};
5663
5664iMapsManager.isFullScreen = false;
5665/**
5666 * Main app file.  Initializes app components.
5667 */
5668
5669/**
5670 * The main app object.
5671 *
5672 */
5673
5674var iMaps = {
5675  originalData: JSON.parse(JSON.stringify(iMapsData)),
5676};
5677
5678iMaps.reset = function () {
5679  if (iMaps.maps) {
5680    Object.keys(iMaps.maps).forEach(function (id) {
5681      iMaps.maps[id].map.dispose();
5682    });
5683  }
5684
5685  iMapsData = iMaps.originalData;
5686  iMaps.init();
5687};
5688/**
5689 * Initializes the interactiveMaps app
5690 *
5691 */
5692
5693iMaps.init = function (hold) {
5694  // if map should not render
5695  if (
5696    typeof hold === 'undefined' &&
5697    typeof iMapsData.options !== 'undefined' &&
5698    typeof iMapsData.options.hold !== 'undefined' &&
5699    iMapsData.options.hold === '1'
5700  ) {
5701    hold = true;
5702  }
5703
5704  if (hold) {
5705    return;
5706  }
5707
5708  if (typeof am4core === 'undefined') {
5709    console.log('Map files not loaded properly.');
5710
5711    let oxygen = document.querySelector('.oxygen-body .map_wrapper .map_render');
5712    if (oxygen) {
5713      oxygen.innerHTML =
5714        'Map Container. <br> Map will not render in Oxygen preview, but will render in live page.<br>Consider enabling the "Async Loading" option in the Settings > Performance page.';
5715    }
5716    return;
5717  }
5718
5719  am4core.ready(function () {
5720    var data;
5721    am4core.options.autoSetClassName = true;
5722    am4core.options.classNamePrefix = 'imaps';
5723    am4core.options.commercialLicense = true;
5724    am4core.options.queue = true;
5725
5726    if (
5727      typeof iMapsData.options !== 'undefined' &&
5728      typeof iMapsData.options.lazyLoad !== 'undefined' &&
5729      iMapsData.options.lazyLoad === '1'
5730    ) {
5731      am4core.options.onlyShowOnViewport = true;
5732    } // animated
5733
5734    if (
5735      typeof iMapsData.options !== 'undefined' &&
5736      typeof iMapsData.options.animations !== 'undefined' &&
5737      (iMapsData.options.animations === '1' || iMapsData.options.animations === true)
5738    ) {
5739      am4core.useTheme(am4themes_animated);
5740    }
5741
5742    data = iMapsModel.prepareData(iMapsData.data);
5743    data.forEach(function (data, index) {
5744      if (index.disabled) {
5745        return;
5746      }
5747
5748      iMapsManager.init(index);
5749    });
5750    iMaps.maps = iMapsManager.maps;
5751  });
5752};
5753
5754iMaps.loadScript = function (url, callback) {
5755  var script = document.createElement('script');
5756  script.type = 'text/javascript';
5757  script.src = url;
5758  script.onreadystatechange = callback;
5759  script.onload = callback;
5760  document.head.appendChild(script);
5761};
5762
5763iMaps.loadScripts = function (urls, callback) {
5764  var loadedCount = 0;
5765
5766  var multiCallback = function multiCallback() {
5767    loadedCount++;
5768
5769    if (loadedCount >= urls.length) {
5770      callback.call(this, arguments);
5771    }
5772  };
5773
5774  urls.forEach(function (url) {
5775    iMaps.loadScript(url, multiCallback);
5776  });
5777};
5778
5779if (
5780  typeof iMapsData.async !== 'undefined' &&
5781  Array.isArray(iMapsData.async) &&
5782  iMapsData.async.length > 0
5783) {
5784  // we need to load the core file first
5785  iMaps.loadScript(iMapsData.async[0], function () {
5786    // then we load the rest - shift() removes the first entry
5787    iMapsData.async.shift();
5788    iMaps.loadScripts(iMapsData.async, function () {
5789      iMaps.init();
5790    });
5791  });
5792} else {
5793  iMaps.init();
5794}

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.