PageSourceSearch

https://ipib.org.br/wp-content/plugins/interactive-geo-maps-premiu…/public/map-service/app.js?ver=1.6.28

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

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.