"use strict";

function _typeof(obj) {
  "@babel/helpers - typeof";
  if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
    _typeof = function _typeof(obj) {
      return typeof obj;
    };
  } else {
    _typeof = function _typeof(obj) {
      return obj &&
        typeof Symbol === "function" &&
        obj.constructor === Symbol &&
        obj !== Symbol.prototype
        ? "symbol"
        : typeof obj;
    };
  }
  return _typeof(obj);
}

/**
 * Geocluster function. Original:
 * https://github.com/yetzt/node-geocluster
 * and adapted to work with iMaps
 */
function geocluster(elements, bias, defaults, tooltipTemplate) {
  bias = parseFloat(bias);
  if (!(this instanceof geocluster))
    return new geocluster(elements, bias, defaults, tooltipTemplate);
  return this._cluster(elements, bias, defaults, tooltipTemplate);
} // degrees to radians

geocluster.prototype._toRad = function (number) {
  return (number * Math.PI) / 180;
}; // geodetic distance approximation

geocluster.prototype._dist = function (lat1, lon1, lat2, lon2) {
  var dlat = this._toRad(lat2 - lat1);

  var dlon = this._toRad(lon2 - lon1);

  var a =
    Math.sin(dlat / 2) * Math.sin(dlat / 2) +
    Math.sin(dlon / 2) *
      Math.sin(dlon / 2) *
      Math.cos(this._toRad(lat1)) *
      Math.cos(this._toRad(lat2));
  return (
    Math.round(2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) * 6371 * 100) /
    100
  );
};

geocluster.prototype._centroid = function (set) {
  var cetroidObj = Object.values(set).reduce(
    function (accumulator, currentValue) {
      return {
        latitude: accumulator.latitude + currentValue.latitude,
        longitude: accumulator.longitude + currentValue.longitude,
      };
    },
    {
      latitude: 0,
      longitude: 0,
    }
  );
  cetroidObj = Object.values(cetroidObj).map(function (e) {
    return e / Object.keys(set).length;
  });
  return cetroidObj;
};

geocluster.prototype._clean = function (data) {
  return data.map(function (cluster) {
    return [cluster.centroid, cluster.elements];
  });
};

geocluster.prototype._cluster = function (
  elements,
  bias,
  defaults,
  tooltipTemplate
) {
  var self = this,
    cluster_map_collection = [];
  var tempMarker; // set bias to 1 on default

  if (typeof bias !== "number" || isNaN(bias)) bias = 1;
  var tot_diff = 0;
  var diffs = [];
  var diff; // calculate sum of differences

  for (var i = 1; i < elements.length; i++) {
    diff = self._dist(
      elements[i].latitude,
      elements[i].longitude,
      elements[i - 1].latitude,
      elements[i - 1].longitude
    );
    tot_diff += diff;
    diffs.push(diff);
  }

  // calculate mean diff
  var mean_diff = tot_diff / diffs.length;
  var diff_variance = 0;

  // calculate variance total
  diffs.forEach(function (diff) {
    diff_variance += Math.pow(diff - mean_diff, 2);
  });

  // derive threshold from stdev and bias - modified to allow bias to be more decisive
  var diff_stdev = Math.sqrt(diff_variance / diffs.length);
  var threshold = 10000 * bias;
  var cluster_map = [];

  // generate random initial cluster map
  cluster_map.push({
    centroid: elements[Math.floor(0.5 * elements.length)],
    elements: [],
    fill: defaults.fill,
    hover: defaults.hover,
    radius: defaults.radius,
  });

  // loop elements and distribute them to clusters
  var changing = true;

  while (changing === true) {
    var new_cluster = false;
    var cluster_changed = false; // iterate over elements

    elements.forEach(function (e, ei) {
      var closest_dist = Infinity;
      var closest_cluster = null; // find closest cluster

      cluster_map.forEach(function (cluster, ci) {
        // distance to cluster
        var dist = self._dist(
          e.latitude,
          e.longitude,
          cluster_map[ci].centroid.latitude,
          cluster_map[ci].centroid.longitude
        );

        if (dist < closest_dist) {
          closest_dist = dist;
          closest_cluster = ci;
        }
      }); // is the closest distance smaller than the stddev of elements?

      if (closest_dist < threshold || closest_dist === 0) {
        // put element into existing cluster
        cluster_map[closest_cluster].elements.push(e);
      } else {
        // create a new cluster with this element
        cluster_map.push({
          centroid: e,
          elements: [e],
        });
        new_cluster = true;
      }
    }); // delete empty clusters from cluster_map

    cluster_map = cluster_map.filter(function (cluster) {
      return cluster.elements.length > 0;
    }); // calculate the clusters centroids and check for change

    cluster_map.forEach(function (cluster, ci) {
      var centroid = self._centroid(cluster.elements);

      changing = false;

      if (
        centroid.latitude !== cluster.centroid.latitude ||
        centroid.longitude !== cluster.centroid.longitude
      ) {
        cluster_map[ci].centroid = centroid;
        cluster_changed = true;
      }
    }); // loop cycle if clusters have changed

    if (!cluster_changed && !new_cluster) {
      changing = false;
    } else {
      // remove all elements from clusters and run again
      if (changing)
        cluster_map = cluster_map.map(function (cluster) {
          cluster.elements = [];
          return cluster;
        });
    }
  }

  cluster_map = cluster_map.map(function (cluster) {
    if (cluster.elements.length === 1) {
      cluster_map_collection.push(cluster.elements[0]);
    } else {
      tempMarker = {
        id: "",
        label: cluster.elements.length,
        name: "",
        value: cluster.elements.length,
        cluster: true,
        latitude: cluster.centroid[0],
        longitude: cluster.centroid[1],
        elements: cluster.elements,
        content: "",
        fill: defaults.fill,
        hover: defaults.hover,
        radius: defaults.radius,
        action: "",
      }; // in case we have a custom tooltipTemplate

      if (tooltipTemplate) {
        tempMarker.tooltipTemplate = tooltipTemplate;
      }

      cluster_map_collection.push(tempMarker);
    }
  }); // compress result

  return cluster_map_collection;
};
/* ROUTER */
var iMapsRouter = {};

iMapsRouter.getGeoFiles = function (regionClicked) {
  var regionID = regionClicked.id.toString(),
    regionName = regionClicked.name,
    urlappend,
    varappend,
    geoFiles = {},
    continents = [
      "southAmerica",
      "northAmerica",
      "europe",
      "middleEast",
      "asia",
      "oceania",
      "africa",
      "antarctica",
    ]; // continents

  if (continents.includes(regionID)) {
    urlappend = "region/world/";
    varappend = "_region_world_";
  } // us maps
  else if (regionID.includes("US-")) {
    urlappend = "region/usa/";
    varappend = "_region_usa_";
    regionID = regionID.substr(-2).toLowerCase();
  } else {
    urlappend = "";
    varappend = "_";
    regionID = regionName.toLowerCase().replace("united states", "usa");
  }

  geoFiles.src =
    "https://cdn.amcharts.com/lib/4/geodata/" + urlappend + regionID + "Low.js";
  geoFiles.map = "am4geodata" + varappend + regionID + "Low";
  geoFiles.title = regionName;
  return geoFiles;
};

iMapsRouter.getAllSrc = function () {
  var sources = {},
    paths = {
      main: "../../vendor/geodata",
      usa: "../../vendor/geodata/region/usa",
      world: "../../vendor/geodata/region/world",
    };

  var fs = require("fs");

  Object.keys(paths).forEach(function (item) {
    sources[item] = [];
    fs.readdir(paths[item], function (err, files) {
      files.forEach(function (file) {
        sources[item].push(file);
      });
    });
  });
  return sources;
};
/**
 * get map am4charts variable name based on name value
 */

iMapsRouter.getVarByName = function (variable) {
  if (typeof variable === "undefined") {
    return "am4geodata_worldLow";
  } else if (variable.includes("custom")) {
    return variable;
  }

  variable = variable.replace(/\//g, "_");
  return "am4geodata_" + variable;
};

iMapsRouter.getCleanMapName = function (mapName, id) {
  if (typeof mapName === "undefined") {
    return false;
  }

  mapName = mapName.replace("Low", "");
  mapName = mapName.replace("High", "");

  if (mapName === "custom") {
    mapName += id;
  }

  return mapName;
};

iMapsRouter.iso2cleanName = function (iso, mapID) {
  var countries = iMapsRouter.getCountries();
  var continents = [
    "africa",
    "antarctiva",
    "asia",
    "europe",
    "middleEast",
    "northAmerica",
    "oceania",
    "southAmerica",
    "centralAmerica",
  ];
  var tempIso;
  var series = iMapsManager.maps[mapID].seriesIndex;
  var match = false;
  console.log("ISO: " + iso); // exceptions

  if ("VA" === iso) {
    return "vatican";
  } else if ("US" === iso) {
    return "usa";
  } else if (iso.includes("CA-")) {
    return "region/canada/" + iso.replace("CA-", "").toLowerCase();
  } else if (iso.includes("MX-")) {
    return "region/mexico/" + iso.replace("MX-", "").toLowerCase();
  } else if (continents.includes(iso)) {
    return "region/world/" + iso;
  } else if (iso.includes("US-")) {
    tempIso = [
      "region/usa/" + iso.replace("US-", "").toLowerCase(),
      "region/usa/congressional/" + iso.replace("US-", "").toLowerCase(),
      "region/usa/congressional2022/" + iso.replace("US-", "").toLowerCase(),
    ];
  } else if ("GB" === iso) {
    tempIso = ["uk", "ukCountries", "ukCounties"];
  } else if ("BA" === iso) {
    tempIso = ["bosniaHerzegovinaCantons", "bosniaHerzegovina"];
  } else if ("IL" === iso) {
    tempIso = ["israel", "israelPalestine"];
  } else if ("SI" === iso) {
    tempIso = ["sloveniaRegions", "slovenia"];
  } else if ("IN" === iso) {
    tempIso = ["india2019", "india", "india2020"];
  } else if ("AE" === iso) {
    tempIso = ["unitedArabEmirates", "uae"];
  } else if ("FR" === iso) {
    tempIso = ["franceDepartments", "france"];
  } else if ("MG" === iso) {
    tempIso = ["madagascarProvince", "madagascarRegion"];
  } else if ("PT" === iso) {
    tempIso = ["portugal", "portugalRegions"];
  } else if ("RS" === iso) {
    tempIso = ["serbia", "serbiaNoKosovo"];
  } else if ("CD" === iso) {
    tempIso = ["congoDR"];
  } else if ("CG" === iso) {
    tempIso = ["congo"];
  } else if ("CZ" === iso) {
    tempIso = ["czechRepublic", "czechia"];
  } else if ("MM" === iso) {
    tempIso = ["myanmar"];
  } // the rest

  // this block needs reviewing.. if/else are a mess
  if (Array.isArray(tempIso)) {
    tempIso.forEach(function (item, index) {
      if (series.hasOwnProperty(item)) {
        match = item;
      }
    });

    if (match) {
      return match;
    } else {
      if (countries.hasOwnProperty(iso)) {
        return iMapsRouter.camelize(countries[iso]);
      }
    }
  } else {
    if (countries.hasOwnProperty(iso)) {
      return iMapsRouter.camelize(countries[iso]);
    }
  }
  return false;
};

iMapsRouter.getCountries = function () {
  var rawjson =
    '{"AF":"Afghanistan","AX":"\xC5land Islands","AL":"Albania","DZ":"Algeria","AS":"American Samoa","AD":"Andorra","AO":"Angola","AI":"Anguilla","AQ":"Antarctica","AG":"Antigua & Barbuda","AR":"Argentina","AM":"Armenia","AW":"Aruba","AC":"Ascension Island","AU":"Australia","AT":"Austria","AZ":"Azerbaijan","BS":"Bahamas","BH":"Bahrain","BD":"Bangladesh","BB":"Barbados","BY":"Belarus","BE":"Belgium","BZ":"Belize","BJ":"Benin","BM":"Bermuda","BT":"Bhutan","BO":"Bolivia","BA":"Bosnia & Herzegovina","BW":"Botswana","BR":"Brazil","IO":"British Indian Ocean Territory","VG":"British Virgin Islands","BN":"Brunei","BG":"Bulgaria","BF":"Burkina Faso","BI":"Burundi","KH":"Cambodia","CM":"Cameroon","CA":"Canada","IC":"Canary Islands","CV":"Cape Verde","BQ":"Caribbean Netherlands","KY":"Cayman Islands","CF":"Central African Republic","EA":"Ceuta & Melilla","TD":"Chad","CL":"Chile","CN":"China","CX":"Christmas Island","CC":"Cocos (Keeling) Islands","CO":"Colombia","KM":"Comoros","CG":"Congo - Brazzaville","CD":"Congo - Kinshasa","CK":"Cook Islands","CR":"Costa Rica","CI":"C\xF4te d\u2019Ivoire","HR":"Croatia","CU":"Cuba","CW":"Cura\xE7ao","CY":"Cyprus","CZ":"Czechia","DK":"Denmark","DG":"Diego Garcia","DJ":"Djibouti","DM":"Dominica","DO":"Dominican Republic","EC":"Ecuador","EG":"Egypt","SV":"El Salvador","GQ":"Equatorial Guinea","ER":"Eritrea","EE":"Estonia","SZ":"Eswatini","ET":"Ethiopia","FK":"Falkland Islands","FO":"Faroe Islands","FJ":"Fiji","FI":"Finland","FR":"France","GF":"French Guiana","PF":"French Polynesia","TF":"French Southern Territories","GA":"Gabon","GM":"Gambia","GE":"Georgia","DE":"Germany","GH":"Ghana","GI":"Gibraltar","GR":"Greece","GL":"Greenland","GD":"Grenada","GP":"Guadeloupe","GU":"Guam","GT":"Guatemala","GG":"Guernsey","GN":"Guinea","GW":"Guinea-Bissau","GY":"Guyana","HT":"Haiti","HN":"Honduras","HK":"Hong Kong SAR China","HU":"Hungary","IS":"Iceland","IN":"India","ID":"Indonesia","IR":"Iran","IQ":"Iraq","IE":"Ireland","IM":"Isle of Man","IL":"Israel","IT":"Italy","JM":"Jamaica","JP":"Japan","JE":"Jersey","JO":"Jordan","KZ":"Kazakhstan","KE":"Kenya","KI":"Kiribati","XK":"Kosovo","KW":"Kuwait","KG":"Kyrgyzstan","LA":"Laos","LV":"Latvia","LB":"Lebanon","LS":"Lesotho","LR":"Liberia","LY":"Libya","LI":"Liechtenstein","LT":"Lithuania","LU":"Luxembourg","MO":"Macao SAR China","MG":"Madagascar","MW":"Malawi","MY":"Malaysia","MV":"Maldives","ML":"Mali","MT":"Malta","MH":"Marshall Islands","MQ":"Martinique","MR":"Mauritania","MU":"Mauritius","YT":"Mayotte","MX":"Mexico","FM":"Micronesia","MD":"Moldova","MC":"Monaco","MN":"Mongolia","ME":"Montenegro","MS":"Montserrat","MA":"Morocco","MZ":"Mozambique","MM":"Myanmar (Burma)","NA":"Namibia","NR":"Nauru","NP":"Nepal","NL":"Netherlands","NC":"New Caledonia","NZ":"New Zealand","NI":"Nicaragua","NE":"Niger","NG":"Nigeria","NU":"Niue","NF":"Norfolk Island","KP":"North Korea","MK":"North Macedonia","MP":"Northern Mariana Islands","NO":"Norway","OM":"Oman","PK":"Pakistan","PW":"Palau","PS":"Palestinian Territories","PA":"Panama","PG":"Papua New Guinea","PY":"Paraguay","PE":"Peru","PH":"Philippines","PN":"Pitcairn Islands","PL":"Poland","PT":"Portugal","XA":"Pseudo-Accents","XB":"Pseudo-Bidi","PR":"Puerto Rico","QA":"Qatar","RE":"R\xE9union","RO":"Romania","RU":"Russia","RW":"Rwanda","WS":"Samoa","SM":"San Marino","ST":"S\xE3o Tom\xE9 & Pr\xEDncipe","SA":"Saudi Arabia","SN":"Senegal","RS":"Serbia","SC":"Seychelles","SL":"Sierra Leone","SG":"Singapore","SX":"Sint Maarten","SK":"Slovakia","SI":"Slovenia","SB":"Solomon Islands","SO":"Somalia","ZA":"South Africa","GS":"South Georgia & South Sandwich Islands","KR":"South Korea","SS":"South Sudan","ES":"Spain","LK":"Sri Lanka","BL":"St Barth\xE9lemy","SH":"St Helena","KN":"St Kitts & Nevis","LC":"St Lucia","MF":"St Martin","PM":"St Pierre & Miquelon","VC":"St Vincent & Grenadines","SD":"Sudan","SR":"Suriname","SJ":"Svalbard & Jan Mayen","SE":"Sweden","CH":"Switzerland","SY":"Syria","TW":"Taiwan","TJ":"Tajikistan","TZ":"Tanzania","TH":"Thailand","TL":"Timor-Leste","TG":"Togo","TK":"Tokelau","TO":"Tonga","TT":"Trinidad & Tobago","TA":"Tristan da Cunha","TN":"Tunisia","TR":"Turkey","TM":"Turkmenistan","TC":"Turks & Caicos Islands","TV":"Tuvalu","UG":"Uganda","UA":"Ukraine","AE":"United Arab Emirates","GB":"United Kingdom","US":"United States","UY":"Uruguay","UM":"US Outlying Islands","VI":"US Virgin Islands","UZ":"Uzbekistan","VU":"Vanuatu","VA":"Vatican City","VE":"Venezuela","VN":"Vietnam","WF":"Wallis & Futuna","EH":"Western Sahara","YE":"Yemen","ZM":"Zambia","ZW":"Zimbabwe"}';
  return JSON.parse(rawjson);
};

iMapsRouter.camelize = function (str) {
  return str
    .replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) {
      return index == 0 ? word.toLowerCase() : word.toUpperCase();
    })
    .replace(/\s+/g, "");
};
/**
 * File that contains functions to prepare raw data that arrives in a javascript variable.
 * 1) Convert color strings to color objects
 * */

var iMapsModel = {};

iMapsModel.prepareData = function (fullData) {
  if (!fullData) {
    return fullData;
  }

  if (!Array.isArray(fullData)) {
    return fullData;
  }

  fullData.forEach(function (data, index) {
    fullData[index] = iMapsModel.iterateData(data);

    if (
      Array.isArray(fullData[index].overlay) &&
      fullData[index].overlay.length > 0
    ) {
      fullData[index].overlay.forEach(function (odata, ind) {
        // if the overlay map is the same as base map, only include the active regions
        if (
          fullData[index].overlay[ind].map === fullData[index].map &&
          !iMapsManager.bool(fullData[index].allowEmpty)
        ) {
          fullData[index].overlay[ind].include =
            iMapsModel.prepareOverlayInclude(odata);
        }

        fullData[index].overlay[ind] = iMapsModel.iterateData(odata);
      });
    }
  });
  return fullData;
};

iMapsModel.prepareOverlayInclude = function (data) {
  var includes = data.includes || "";

  if (data.regions) {
    data.regions.forEach(function (region, index) {
      includes += "," + region.id;
    });
  }

  return includes;
};

iMapsModel.iterateData = function (data) {
  // check if there's a custom callback function to prepare the data
  if (typeof igm_custom_filter === "function") {
    data = igm_custom_filter(data);
  } // check if there's a map specific custom callback function

  var mapFunction = "igm_custom_filter_" + data.id;

  if (typeof window[mapFunction] === "function") {
    data = window[mapFunction](data);
  } // create color objects

  data = iMapsModel.prepareColor(data);

  // convert exclude and includes to array
  data = iMapsModel.prepareExcludeIncludes(data);

  // convert coordinates to int
  data = iMapsModel.coordinatesToInt(data);

  // prepare entries that use defaults
  data = iMapsModel.prepareEntriesData(data);

  // prepare grouped regions
  data = iMapsModel.prepareGroupedRegions(data);

  //extract image source from imageMarkers
  data = iMapsModel.prepareImageFields(data);

  //prepare lines multiGeoLine format
  data = iMapsModel.prepareMultiGeoLine(data);
  return data;
};

iMapsModel.prepareMultiGeoLine = function (data) {
  if (Array.isArray(data.lines) && data.lines.length) {
    //make sure we only get entries with geoline data
    data.lines = data.lines.filter(function (line) {
      if (Array.isArray(line.multiGeoLine) && line.multiGeoLine.length) {
        return line;
      }
    });

    data.lines.map(function (line) {
      // notice the arrow option needs to be reversed, because it controls the arrow.disabled option when rendered
      line.arrow =
        typeof line.arrow !== "undefined"
          ? iMapsManager.bool(line.arrow)
          : false;
      line.arrowDisabled = !line.arrow;
      line.multiGeoLine.map(function (geoLine) {
        Object.assign(geoLine, geoLine.coordinates);
        delete geoLine.coordinates;
      });
    });
  }

  return data;
};

iMapsModel.prepareImageFields = function (data) {
  // image markers
  if (Array.isArray(data.imageMarkers) && Array.isArray(data.imageMarkers)) {
    data.imageMarkers.map(function (marker) {
      if (
        typeof marker.href === "undefined" &&
        marker.image &&
        (Array.isArray(marker.image) || typeof marker.image === "object")
      ) {
        marker.href = marker.image.url;
      }
      return marker;
    });
  }

  return data;
};

iMapsModel.prepareGroupedRegions = function (data) {
  var regions, tempRegion, group, groupIds, groupName; // regions

  if (Array.isArray(data.regions) && data.regions.length) {
    if (typeof data.regionGroups === "undefined") {
      data.regionGroups = [];
    }

    // if the option to group entries is enabled, cosider them as a group entry
    if (iMapsManager.bool(data.regionsGroupHover)) {
      data.regionGroups.push(data.regions);
      return data;
    }

    data.regions.map(function (region, index) {
      if (region.id && String(region.id).includes(",")) {
        group = [];
        groupIds = [];
        regions = region.id.split(",");
        groupName = region.name;

        regions.forEach(function (reg, ix) {
          tempRegion = Object.assign({}, region);
          tempRegion.id = reg.trim();

          // keep some original values
          tempRegion.originalID = region.id;
          tempRegion.groupName = groupName;

          // delete the group name, to keep region name
          delete tempRegion.name;
          delete region.name;

          // check if numeric
          if (!isNaN(tempRegion.id)) {
            // tempRegion.id = parseFloat( tempRegion.id );
          }

          // check if not repeated
          if (!groupIds.includes(tempRegion.id)) {
            groupIds.push(tempRegion.id);
            group.push(tempRegion);
          }
        });

        // add group to main data object
        // finally add it to the main data object
        data.regionGroups.push(group);

        // remove this region from the default regions data set
        data.regions.splice(index, -1);
      } else {
        return region;
      }
    });
  }

  return data;
};

iMapsModel.prepareTooltip = function (tooltipContent, options) {
  var maxWidth, isMSIE, images, tooltipNode, range, tempDiv;

  if (typeof options === "undefined" || typeof tooltipContent === "undefined") {
    return tooltipContent;
  }

  maxWidth =
    typeof options.maxWidth !== "undefined" && options.maxWidth !== ""
      ? parseInt(options.maxWidth)
      : false;
  isMSIE = iMapsModel.isMSIE(); // remove html tags for IE

  if (isMSIE) {
    return iMapsModel.removeHTMLtags(tooltipContent);
  } // check for images

  if (tooltipContent && tooltipContent.includes("<img")) {
    // images
    range = document.createRange(); // Make the parent of the first div in the document becomes the context node

    range.selectNode(document.getElementsByTagName("div").item(0));
    tooltipNode = range.createContextualFragment(tooltipContent);
    images = tooltipNode.querySelectorAll("img");

    if (images.length > 0) {
      images.forEach(function (img) {
        if (img.width !== 0 && img.style.width === "") {
          img.style.width = img.width + "px";
        }

        if (img.height !== 0 && img.style.height === "") {
          img.style.height = img.height + "px";
        }
      });
      tempDiv = document.createElement("div");
      tempDiv.appendChild(tooltipNode.cloneNode(true));
      tooltipContent = tempDiv.innerHTML;
    }
  } // tooltip

  if (maxWidth && tooltipContent !== "" && !isMSIE) {
    tooltipContent =
      '<div class="imapsInnerTooltip" style="max-width:' +
      maxWidth +
      'px">' +
      tooltipContent +
      "</div>";
  }

  return tooltipContent;
};

iMapsModel.isMSIE = function () {
  return window.navigator.userAgent.match(/(MSIE|Trident)/);
};

iMapsModel.removeHTMLtags = function (str) {
  if (str === null || str === "") return "";
  else str = str.toString();
  return str.replace(/<[^>]*>/g, "");
};

iMapsModel.prepareEntriesData = function (data) {
  if (data.heatMapMarkers && data.heatMapMarkers.enabled === "1") {
    data.heatMapMarkers.minRadius = parseInt(data.heatMapMarkers.minRadius);
    data.heatMapMarkers.maxRadius = parseInt(data.heatMapMarkers.maxRadius);
  }

  var mapID = data.id; // regions

  if (Array.isArray(data.regions) && data.regions.length) {
    data.regions.map(function (region) {
      if (
        typeof region.useDefaults === "undefined" ||
        region.useDefaults === "1"
      ) {
        Object.assign(region, data.regionDefaults);
      }

      if (typeof region.action !== "undefined" && region.action === "default") {
        region.action = data.regionDefaults.action;
      }

      if (
        typeof data.regionDefaults.triggerClickOnHover !== "undefined" &&
        data.regionDefaults.triggerClickOnHover === "1"
      ) {
        region.triggerClickOnHover = true;
      }

      if (
        typeof data.regionActiveState !== "undefined" &&
        data.regionActiveState.enabled === "1"
      ) {
        region.activeState = true;
      }

      // auto labels properties
      if (
        data.regionLabels &&
        data.regionLabels.source === "custom" &&
        typeof data.regionLabels.customSource !== "undefined"
      ) {
        if (data.regionLabels.customSource.includes(".")) {
          region.autoLabel = data.regionLabels.customSource
            .split(".")
            .reduce(function (obj, i) {
              if (obj[i]) {
                return obj[i];
              }

              return 0;
            }, region);
        } else {
          region.autoLabel = region[data.regionLabels.customSource];
        }
      }

      if (data.heatMapRegions && data.heatMapRegions.enabled === "1") {
        // if it includes a reference with dot notation
        if (data.heatMapRegions.source.includes(".")) {
          region.heatMapRegionRef = data.heatMapRegions.source
            .split(".")
            .reduce(function (obj, i) {
              if (obj[i]) {
                return obj[i];
              }

              return 0;
            }, region);
        }

        delete region.fill;
      } // default action

      // border hover - currently doesn't exist
      if (
        typeof data.visual.borderColorHover !== "undefined" &&
        data.visual.borderColorHover !== data.visual.borderColor
      ) {
        region.borderColorHover = data.visual.borderColorHover;
      } // border width on hover - currently doesn't exist

      if (
        typeof data.visual.borderWidthHover !== "undefined" &&
        data.visual.borderWidthHover !== data.visual.borderWidth
      ) {
        region.borderWidthHover = data.visual.borderWidthHover;
      }

      region.tooltipContent = iMapsModel.prepareTooltip(
        region.tooltipContent,
        data.tooltip
      );
      region.mapID = mapID;

      // fix for value with "-" minus sign
      if (
        region.value &&
        _typeof(region.value) !== undefined &&
        region.value !== ""
      ) {
        region.val = region.value;
        region.value = parseFloat(region.value);
      }

      return region;
    }); // heatmap with dot notation
    // if it includes a reference with dot notation

    if (data.heatMapRegions && data.heatMapRegions.source.includes(".")) {
      data.heatMapRegions.source = "heatMapRegionRef";
    }
  } // round markers

  if (Array.isArray(data.roundMarkers) && data.roundMarkers.length) {
    data.roundMarkers.map(function (marker) {
      if (
        typeof marker.useDefaults === "undefined" ||
        marker.useDefaults === "1"
      ) {
        Object.assign(marker, data.markerDefaults);
      }

      if (marker.coordinates) {
        marker.latitude = marker.coordinates.latitude;
        marker.longitude = marker.coordinates.longitude; //Object.assign(marker, marker.coordinates);
        //delete marker.coordinates;
      } // set name to be id

      if (typeof marker.latitude === "string") {
        marker.latitude = parseFloat(marker.latitude);
      }
      if (typeof marker.longitude === "string") {
        marker.longitude = parseFloat(marker.latitude);
      }

      if (typeof marker.name === "undefined") {
        marker.name = marker.id;
      }

      if (
        data.roundMarkersMobileSize &&
        parseInt(data.roundMarkersMobileSize) !== 100
      ) {
        if (window.innerWidth <= 780) {
          marker.radius =
            (parseFloat(marker.radius) *
              parseFloat(data.roundMarkersMobileSize)) /
            100;
        }
      }

      if (data.heatMapMarkers && data.heatMapMarkers.enabled === "1") {
        if (!isNaN(marker.value)) {
          marker.value = parseFloat(marker.value);
        } // if it includes a reference with dot notation

        if (data.heatMapMarkers.source.includes(".")) {
          marker.heatMapMarkerRef = data.heatMapMarkers.source
            .split(".")
            .reduce(function (obj, i) {
              if (obj[i]) {
                return obj[i];
              }

              return 0;
            }, marker);
        }

        delete marker.fill;
        delete marker.radius;
      }

      // fix for value with "-" minus sign
      if (
        marker.value &&
        _typeof(marker.value) !== undefined &&
        marker.value !== ""
      ) {
        marker.val = marker.value;
        marker.value = parseFloat(marker.value);
      }

      // default action
      if (marker.action === "default") {
        marker.action = data.markerDefaults.action;
      }

      if (
        typeof data.markerDefaults.triggerClickOnHover !== "undefined" &&
        data.markerDefaults.triggerClickOnHover === "1"
      ) {
        marker.triggerClickOnHover = true;
      }

      if (
        typeof data.triggerRegionHover !== "undefined" &&
        data.triggerRegionHover.enabled === "1"
      ) {
        marker.triggerRegionHover = true;
      }

      marker.tooltipContent = iMapsModel.prepareTooltip(
        marker.tooltipContent,
        data.tooltip
      );
      marker.mapID = mapID;
      return marker;
    }); // remove markers with empty coordinates

    data.roundMarkers = data.roundMarkers.filter(function (marker) {
      return (
        typeof marker.latitude !== "undefined" &&
        marker.latitude !== "" &&
        marker.name !== ""
      );
    }); // heatmap with dot notation
    // if it includes a reference with dot notation

    if (data.heatMapMarkers && data.heatMapMarkers.source.includes(".")) {
      data.heatMapMarkers.source = "heatMapMarkerRef";
    }
  } // icon markers

  if (Array.isArray(data.iconMarkers) && data.iconMarkers.length) {
    data.iconMarkers.map(function (marker) {
      if (
        typeof marker.useDefaults === "undefined" ||
        marker.useDefaults === "1"
      ) {
        Object.assign(marker, data.iconMarkerDefaults);
      }

      if (marker.coordinates) {
        marker.latitude = marker.coordinates.latitude;
        marker.longitude = marker.coordinates.longitude; //Object.assign(marker, marker.coordinates);
        //delete marker.coordinates;
      } // set name to be id

      if (typeof marker.name === "undefined") {
        marker.name = marker.id;
      } // default action

      if (marker.action === "default") {
        marker.action = data.iconMarkerDefaults.action;
      }

      if (
        typeof data.iconMarkerDefaults.triggerClickOnHover !== "undefined" &&
        data.iconMarkerDefaults.triggerClickOnHover === "1"
      ) {
        marker.triggerClickOnHover = true;
      }

      // Possible approach to resize icon marker on smaller screens, but won't resize on the fly

      if (
        data.iconMarkersMobileSize &&
        parseInt(data.iconMarkersMobileSize) !== 100
      ) {
        if (window.innerWidth <= 780) {
          marker.scale =
            (parseFloat(marker.scale) *
              parseFloat(data.iconMarkersMobileSize)) /
            100;
        }
      }

      // solve issue with value parameter only accepting numbers
      marker.val = marker.value;
      marker.value = parseFloat(marker.value);

      marker.tooltipContent = iMapsModel.prepareTooltip(
        marker.tooltipContent,
        data.tooltip
      );
      marker.mapID = mapID;
      return marker;
    });
  } // image markers

  if (Array.isArray(data.imageMarkers) && data.imageMarkers.length) {
    data.imageMarkers.map(function (marker) {
      if (
        typeof marker.useDefaults === "undefined" ||
        marker.useDefaults === "1"
      ) {
        Object.assign(marker, data.imageMarkerDefaults);
      }

      if (marker.coordinates) {
        marker.latitude = marker.coordinates.latitude;
        marker.longitude = marker.coordinates.longitude; //Object.assign(marker, marker.coordinates);
        //delete marker.coordinates;
      } // set name to be id

      if (typeof marker.nonScaling !== "undefined") {
        marker.nonScaling = iMapsManager.bool(marker.nonScaling);
      }

      if (typeof marker.size === "undefined") {
        marker.size = data.imageMarkerDefaults.size;
      }

      if (typeof marker.horizontalCenter === "undefined") {
        marker.horizontalCenter = data.imageMarkerDefaults.horizontalCenter;
      }

      if (typeof marker.verticalCenter === "undefined") {
        marker.verticalCenter = data.imageMarkerDefaults.verticalCenter;
      }

      if (typeof marker.name === "undefined") {
        marker.name = marker.id;
      }

      if (marker.action === "default" || typeof marker.action === "undefined") {
        marker.action = data.imageMarkerDefaults.action;
      }

      if (
        typeof data.imageMarkerDefaults.triggerClickOnHover !== "undefined" &&
        data.imageMarkerDefaults.triggerClickOnHover === "1"
      ) {
        marker.triggerClickOnHover = true;
      }

      // Possible approach to resize icon marker on smaller screens, but won't resize on the fly

      if (
        data.imageMarkersMobileSize &&
        parseInt(data.imageMarkersMobileSize) !== 100
      ) {
        if (window.innerWidth <= 780) {
          marker.size =
            (parseFloat(marker.size) *
              parseFloat(data.imageMarkersMobileSize)) /
            100;
        }
      }

      // solve problem of value param only accepting numbers
      marker.val = marker.value;
      marker.value = parseFloat(marker.value);

      marker.tooltipContent = iMapsModel.prepareTooltip(
        marker.tooltipContent,
        data.tooltip
      );
      marker.mapID = mapID;
      return marker;
    });
  } // labels

  if (Array.isArray(data.labels) && data.labels.length) {
    data.labels.map(function (label) {
      if (
        typeof label.useDefaults === "undefined" ||
        label.useDefaults === "1"
      ) {
        Object.assign(label, data.labelDefaults);
        Object.assign(label, data.labelPosition);
      }

      if (label.coordinates) {
        Object.assign(label, label.coordinates);
        delete label.coordinates;
      }

      if (label.action === "default") {
        label.action = data.labelDefaults.action;
      }

      if (
        typeof data.labelDefaults.triggerClickOnHover !== "undefined" &&
        data.labelDefaults.triggerClickOnHover === "1"
      ) {
        label.triggerClickOnHover = true;
      }

      if (typeof label.size !== "undefined") {
        label.fontSize = label.size;
      } // change font size on smaller screens

      if (data.labelsMobileSize && parseInt(data.labelsMobileSize) !== 100) {
        if (window.innerWidth <= 780) {
          label.fontSize =
            (parseInt(label.fontSize) * parseInt(data.labelsMobileSize)) / 100;
          label.size = label.fontSize;
        }
      }

      label.tooltipContent = iMapsModel.prepareTooltip(
        label.tooltipContent,
        data.tooltip
      );
      label.mapID = mapID;
      return label;
    });
  } // lines

  if (Array.isArray(data.lines) && data.lines.length) {
    data.lines.map(function (line) {
      if (typeof line.useDefaults === "undefined" || line.useDefaults === "1") {
        Object.assign(line, data.lineDefaults);
      }

      line.curvature = parseFloat(line.curvature);
      return line;
    });
  }

  return data;
};

iMapsModel.prepareExcludeIncludes = function (data) {
  if (
    typeof data.onlyIncludeActive !== "undefined" &&
    parseInt(data.onlyIncludeActive) === 1
  ) {
    data.include = [];
    data.exclude = [];

    if (data.regions) {
      data.regions.forEach(function (region, index) {
        data.include.push(region.id);

        if (!isNaN(region.id)) {
          data.include.push(parseInt(region.id));
        }
      });
    }

    return data;
  }

  if (
    data.exclude &&
    typeof data.exclude === "string" &&
    data.exclude.trim().length
  ) {
    data.exclude = data.exclude.split(",").map(function (item) {
      return item.trim();
    });
  }

  if (data.include && data.include.trim().length) {
    data.include = data.include.split(",").map(function (item) {
      return item.trim();
    });

    data.include.map(function (item) {
      if (!isNaN(item)) {
        data.include.push(parseInt(item));
      }
    });
  }

  return data;
};

iMapsModel.coordinatesToInt = function (data) {
  var convertCoordinates = function convertCoordinates(key, obj) {
      obj[key].latitude = Number(obj[key].latitude);
      obj[key].longitude = Number(obj[key].longitude);
    },
    iterateObj = function iterateObj(Obj) {
      if (_typeof(Obj) !== "object" || Obj === null) {
        return;
      }

      Object.keys(Obj).map(function (key, index) {
        if (_typeof(Obj[key]) === "object") {
          if (key === "coordinates" || key === "homeGeoPoint") {
            convertCoordinates(key, Obj);
          } else if (typeof Obj["className"] !== "undefined") {
            return;
          } else {
            iterateObj(Obj[key]);
          }
        }
      });
    };

  iterateObj(data);
  return data;
};

iMapsModel.prepareColor = function (data) {
  // prepare color fields
  var colorFields = [
      "inactiveColor",
      "activeColor",
      "hoverColor",
      "hover",
      "inactiveHoverColor",
      "backgroundColor",
      "color",
      "minColor",
      "maxColor",
      "fill",
      "projectionBackgroundColor",
      "borderColor",
      "borderColorHover",
    ],
    createGradient = function createGradient(data) {
      var colours = data.split("|");
      var gradient, gradientType, gradientOffset, colourIndex;

      gradientType =
        typeof igmGradientType !== "undefined"
          ? igmGradientType
          : "LinearGradient";
      gradientOffset =
        typeof igmGradientOffset !== "undefined" &&
        Array.isArray(igmGradientOffset)
          ? igmGradientOffset
          : [];

      gradient = new am4core[gradientType]();
      colours.forEach(function (color, index) {
        gradient.addColor(am4core.color(color), 1, gradientOffset[index]);
      });

      //rotation
      gradient.rotation =
        typeof igmGradientRotation !== "undefined" ? igmGradientRotation : 0;
      return gradient;
    },
    createPattern = function createPattern(data) {
      var url = data;
      // Create pattern
      var pattern = new am4core.Pattern();
      pattern.width = 150;
      pattern.height = 150;
      pattern.strokeWidth = 0;
      pattern.stroke = am4core.color("#6699cc");
      //pattern.patternUnits = 'objectBoundingBox'; // objectBoundingBox // userSpaceOnUse

      var image = new am4core.Image();
      image.href = url;
      image.width = 150;
      image.height = 150;
      image.x = 0;
      image.y = 0;
      image.verticalCenter = "middle";
      image.valign = "middle";
      pattern.addElement(image.element);
      pattern.addElement(image.element);
      pattern.addElement(image.element);
      return pattern;
    },
    checkColor = function checkColor(key, obj) {
      if (colorFields.includes(key)) {
        if (obj[key].includes("|")) {
          obj[key] = createGradient(obj[key]);
        } else if (obj[key].startsWith("http")) {
          obj[key] = createPattern(obj[key]);
        }
        // on hover out, amcharts is not able to resolve transparent colours, in default states, so we prevent this
        // by setting an almost transparent colour
        else if (
          key === "hover" &&
          (obj[key] === "transparent" || obj[key].endsWith(",0)"))
        ) {
          obj[key] = am4core.color("rgba(0,0,0,0.001)");
        } else if (obj[key] === "transparent") {
          obj[key] = am4core.color("rgba(0,0,0,0)");
        } else {
          obj[key] = am4core.color(obj[key]);
        }
      }
    },
    iterateObj = function iterateObj(Obj) {
      if (_typeof(Obj) !== "object" || Obj === null) {
        return;
      }

      Object.keys(Obj).map(function (key, index) {
        if (_typeof(Obj[key]) === "object") {
          iterateObj(Obj[key]);
        } else if (typeof Obj["className"] !== "undefined") {
          return;
        } else {
          checkColor(key, Obj);
        }
      });
    };

  iterateObj(data);
  return data;
};

/**
 * Retrives object with region codes and names from geojson
 */

iMapsModel.extractCodes = function (data) {
  var obj = {};

  for (var i = 0; i < data.features.length; i++) {
    obj[data.features[i].properties.id] = data.features[i].properties.name;
  }

  return obj;
};

("use strict");
/* jshint node: true */

/* global Promise */

/* jshint browser: true */

/* global am4maps */

/* global am4core */

/* global iMapsData */

/* global geocluster */

/* global iMapsRouter */

/* global iMaps */

/* jshint esversion:5 */

/* global iMapsActions */

/*
Model Object with methods and helpers to build the maps
*/

var iMapsManager = {};
iMapsManager.maps = {};

iMapsManager.init = function (i) {
  var im = this;
  im.addMap(i);
};
/**
 * Adds maps based on data index
 *
 */

iMapsManager.addMap = function (index) {
  var im = this,
    data = iMapsData.data[index],
    id = data.id,
    map,
    regionSeries,
    groupedSeries,
    markerSeries,
    labelSeries,
    lineSeries,
    clusters,
    mapContainer,
    seriesColumn,
    legendHover,
    legendActive,
    customLegend,
    imageSeries,
    iconSeries,
    graticuleSeries,
    clusterSeries,
    mapVar,
    bgSeries,
    bgImage,
    aspRatioContainer,
    container = document.getElementById(data.container);

  if (data.disabled) {
    return;
  }

  if (container === null) {
    return;
  }

  aspRatioContainer = container.closest(".map_aspect_ratio");

  // if map was already built
  if (typeof im.maps[id] !== "undefined") {
    im.maps[id].map.dispose();
  }

  // map container size adjustment
  // if mobile
  if (
    window.innerWidth <= 780 &&
    typeof data.visual.paddingTop !== "undefined" &&
    data.visual.paddingTop !== ""
  ) {
    aspRatioContainer.style.paddingTop =
      String(data.visual.paddingTopMobile) + "%";
  } else {
    aspRatioContainer.style.paddingTop = String(data.visual.paddingTop) + "%";
  }

  window.addEventListener("resize", function () {
    if (
      window.innerWidth <= 780 &&
      typeof data.visual.paddingTop !== "undefined" &&
      data.visual.paddingTop !== ""
    ) {
      aspRatioContainer.style.paddingTop =
        String(data.visual.paddingTopMobile) + "%";
    } else {
      aspRatioContainer.style.paddingTop = String(data.visual.paddingTop) + "%";
    }
  });

  if (data.visual.maxWidth !== "") {
    //container.closest(".map_box").style.maxWidth = String(data.visual.maxWidth) + "px";
  }

  if (data.visual.fontFamily !== "" && data.visual.fontFamily !== "inherit") {
    container.closest(".map_box").style.fontFamily = data.visual.fontFamily;
  }

  // create map and a shorter reference to it
  im.maps[id] = {
    map: am4core.create(data.container, am4maps.MapChart),
    series: [],
    clusterSeries: {},
    seriesIndex: {},
    seriesById: {},
    data: data,
    allBaseSeries: [],
    labelSeries: [],
    baseRegionSeries: {},
    groupedBaseRegionSeries: [],
    backgroundSeries: {},
  };
  map = im.maps[id].map;
  map.readerTitle = "Interactive Map";

  clusterSeries = im.maps[id].clusterSeries;
  // ready event
  // on click map debug
  map.events.on("ready", function (ev) {
    var event = new Event("mapready");
    container.dispatchEvent(event);
    // we might move the event that triggers on ready to mapappeared because it doesn't work well with custom maps
    im.triggerOnReady(id, data);
  });
  map.events.on("appeared", function (ev) {
    var event = new Event("mapappeared");
    container.dispatchEvent(event);
    im.triggerOnAppeared(id, data);
    container.classList.remove("map_loading");
  });

  // locale
  if (
    typeof iMapsData.options !== "undefined" &&
    typeof iMapsData.options.locale !== "undefined" &&
    iMapsData.options.locale &&
    typeof window["am4lang_" + iMapsData.options.locale] !== "undefined"
  ) {
    map.language.locale = window["am4lang_" + iMapsData.options.locale];
  } // enable small map

  if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) {
    map.smallMap = new am4maps.SmallMap();
  } // load map geodata

  if (data.map === "custom" || im.bool(data.useGeojson)) {
    map.geodataSource.url = data.mapURL;
  } else {
    mapVar = iMapsRouter.getVarByName(data.map);
    map.geodata = window[mapVar];
  }

  if (
    typeof iMapsData.options !== "undefined" &&
    typeof iMapsData.options.lang !== "undefined" &&
    iMapsData.options.lang &&
    typeof window["am4geodata_lang_" + iMapsData.options.lang] !== "undefined"
  ) {
    map.geodataNames = window["am4geodata_lang_" + iMapsData.options.lang];
  }

  // projection - moved to the end of the function to fix issue with Albers not rendering correctly
  map.projection = new am4maps.projections[data.projection]();

  // fix issues with USA territories map
  if (data.map.startsWith("usaTerritories")) {
    map.events.on("ready", function (ev) {
      map.projection = new am4maps.projections[data.projection]();
    });
  }

  map.fontFamily = data.visual.fontFamily;

  // export menu
  if (data.exportMenu && im.bool(data.exportMenu.enable)) {
    map.exporting.menu = new am4core.ExportMenu();
    map.exporting.menu.items[0].icon =
      "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiA8Zz4KICA8dGl0bGU+YmFja2dyb3VuZDwvdGl0bGU+CiAgPHJlY3QgeD0iLTEiIHk9Ii0xIiB3aWR0aD0iNS45NDQ3NzMiIGhlaWdodD0iNS45NDQ3NzMiIGlkPSJjYW52YXNfYmFja2dyb3VuZCIgZmlsbD0ibm9uZSIvPgogPC9nPgoKIDxnPgogIDx0aXRsZT5MYXllciAxPC90aXRsZT4KICA8cGF0aCBzdHJva2U9Im51bGwiIGQ9Im0xNC40MjcwMzEsMTUuNzQwNDAxcTAsLTAuMjU3NjQzIC0wLjE4ODI3NCwtMC40NDU5MTd0LTAuNDQ1OTE3LC0wLjE4ODI3NHQtMC40NDU5MTcsMC4xODgyNzR0LTAuMTg4Mjc0LDAuNDQ1OTE3dDAuMTg4Mjc0LDAuNDQ1OTE3dDAuNDQ1OTE3LDAuMTg4Mjc0dDAuNDQ1OTE3LC0wLjE4ODI3NHQwLjE4ODI3NCwtMC40NDU5MTd6bTIuNTM2NzgsMHEwLC0wLjI1NzY0MyAtMC4xODgyNzQsLTAuNDQ1OTE3dC0wLjQ0NTkxNywtMC4xODgyNzR0LTAuNDQ1OTE3LDAuMTg4Mjc0dC0wLjE4ODI3NCwwLjQ0NTkxN3QwLjE4ODI3NCwwLjQ0NTkxN3QwLjQ0NTkxNywwLjE4ODI3NHQwLjQ0NTkxNywtMC4xODgyNzR0MC4xODgyNzQsLTAuNDQ1OTE3em0xLjI2ODM5LC0yLjIxOTY4NWwwLDMuMTcwOTdxMCwwLjM5NjM3NCAtMC4yNzc0NjEsMC42NzM4MzR0LTAuNjczODM0LDAuMjc3NDYxbC0xNC41ODY0NywwcS0wLjM5NjM3NCwwIC0wLjY3MzgzNCwtMC4yNzc0NjF0LTAuMjc3NDYxLC0wLjY3MzgzNGwwLC0zLjE3MDk3cTAsLTAuMzk2Mzc0IDAuMjc3NDYxLC0wLjY3MzgzNHQwLjY3MzgzNCwtMC4yNzc0NjFsNC42MDc4MTYsMGwxLjMzNzc1MSwxLjM0NzY1OXEwLjU3NDczOCwwLjU1NDkyMSAxLjM0NzY1OSwwLjU1NDkyMXQxLjM0NzY1OSwtMC41NTQ5MjFsMS4zNDc2NTksLTEuMzQ3NjU5bDQuNTk3OTA4LDBxMC4zOTYzNzQsMCAwLjY3MzgzNCwwLjI3NzQ2MXQwLjI3NzQ2MSwwLjY3MzgzNGwwLjAwMDAxOCwwem0tMy4yMjA1MjMsLTUuNjM4MzlxMC4xNjg0NTYsMC40MDYyODIgLTAuMTM4NzMsMC42OTM2NTFsLTQuNDM5MzYsNC40MzkzNnEtMC4xNzgzNjUsMC4xODgyNzQgLTAuNDQ1OTE3LDAuMTg4Mjc0dC0wLjQ0NTkxNywtMC4xODgyNzRsLTQuNDM5MzYsLTQuNDM5MzZxLTAuMzA3MTg3LC0wLjI4NzM2OSAtMC4xMzg3MywtMC42OTM2NTFxMC4xNjg0NTYsLTAuMzg2NDY1IDAuNTg0NjQ3LC0wLjM4NjQ2NWwyLjUzNjc4LDBsMCwtNC40MzkzNnEwLC0wLjI1NzY0MyAwLjE4ODI3NCwtMC40NDU5MTd0MC40NDU5MTcsLTAuMTg4Mjc0bDIuNTM2NzgsMHEwLjI1NzY0MywwIDAuNDQ1OTE3LDAuMTg4Mjc0dDAuMTg4Mjc0LDAuNDQ1OTE3bDAsNC40MzkzNmwyLjUzNjc4LDBxMC40MTYxOTEsMCAwLjU4NDY0NywwLjM4NjQ2NXoiIGlkPSJzdmdfMSIvPgogPC9nPgo8L3N2Zz4=";
    map.exporting.menu.align = data.exportMenu.align
      ? data.exportMenu.align
      : "right";
    map.exporting.menu.verticalAlign = data.exportMenu.verticalAlign
      ? data.exportMenu.verticalAlign
      : "top";
  }

  // touch devices options
  if (data.touchInterface) {
    if (im.bool(data.touchInterface.tapToActivate)) {
      map.tapToActivate = true;
      map.tapTimeout = data.touchInterface.tapTimeout;
    }

    if (im.bool(data.touchInterface.dragGrip)) {
      map.dragGrip.disabled = false;
      map.dragGrip.autoHideDelay = data.touchInterface.dragGripAutoHideDelay;
    }
  }

  // different map center
  //map.deltaLongitude = -10;
  // pan behaviours
  // map.panBehavior = "rotateLongLat";
  // map.panBehavior = "rotateLong";
  // map.deltaLatitude = -20;
  // map.padding(20, 20, 20, 20);
  // visual settings

  map.background.fill = data.visual.backgroundColor;
  map.background.fillOpacity = data.visual.backgroundOpacity;

  // iOS scroll issue fix - not working
  map.backgroundSeries.mapPolygons.template.focusable = false;
  map.background.focusable = false;
  map.backgroundSeries.focusable = false;

  // backgroud image
  if (
    typeof data.visual.bgImage !== "undefined" &&
    typeof data.visual.bgImage.url !== "undefined" &&
    data.visual.bgImage.url !== ""
  ) {
    bgSeries = map.series.push(new am4maps.MapImageSeries());
    bgSeries.toBack();
    bgImage = bgSeries.mapImages.template.createChild(am4core.Image);
    bgImage.propertyFields.href = "src";
    bgImage.width = map.width;
    bgImage.height = map.height;
    bgSeries.data = [
      {
        src: data.visual.bgImage.url,
      },
    ];

    im.maps[id].backgroundSeries = bgSeries;
  }

  map.exporting.backgroundColor = data.visual.backgroundColor;
  map.exporting.filePrefix = "interactive_map";
  map.exporting.useWebFonts = false;

  // graticulate - grid lines
  if (data.projection === "Orthographic" && data.grid) {
    graticuleSeries = map.series.push(new am4maps.GraticuleSeries());
    graticuleSeries.mapLines.template.line.stroke = data.grid.color;
    graticuleSeries.mapLines.template.line.strokeOpacity = 1;
    graticuleSeries.fitExtent = false;
    map.backgroundSeries.mapPolygons.template.polygon.fillOpacity = 1;
    map.backgroundSeries.mapPolygons.template.polygon.fill =
      data.grid.projectionBackgroundColor;
  }

  im.handleZoom(id); // Auto Legend

  if (data.legend && im.bool(data.legend.enabled)) {
    map.legend = new am4maps.Legend(); // positioing

    if (data.legend.position === "top" || data.legend.position === "bottom") {
      map.legend.contentAlign = data.legend.align;
      map.legend.valign = data.legend.position;
    } else {
      map.legend.position = data.legend.position;
      map.legend.align = data.legend.position;
      map.legend.valign = data.legend.valign;
    }

    if (
      typeof data.legend.style !== "undefined" &&
      data.legend.style !== "default"
    ) {
      im.setLegendStyle(id, map.legend, data.legend.style);
    }

    if (_typeof(data.legend.fill) !== undefined) {
      map.legend.labels.template.fill = data.legend.fill;
    } // interactive

    if (data.legend.clickable === "false") {
      map.legend.itemContainers.template.interactionsEnabled = false;
    }

    if (data.legend.clickable === "toggle") {
      // do nothing, it's the default event
      // let's just clear the events, just in case.
      map.legend.itemContainers.template.events.on("hit", function (ev) {
        iMapsManager.clearSelected(id);
      });
    }

    if (data.legend.clickable === "select") {
      map.legend.itemContainers.template.togglable = false;
      map.legend.itemContainers.template.events.on("hit", function (ev) {
        iMapsManager.clearSelected(id);
        var select = [];
        var seriesType = im.getTargetSeriesType(ev.target.dataItem.dataContext);
        var target = ""; // currently only works for region series

        if (seriesType === "MapPolygonSeries") {
          ev.target.dataItem.dataContext.mapPolygons.each(function (polygon) {
            if (!polygon.dataItem.dataContext.madeFromGeoData) {
              polygon.setState("active");
              polygon.isActive = true;
              polygon.isHover = false;
              select.push(polygon);
            }
          });
          im.maps[id].selected = select;
        }
      });
      map.legend.itemContainers.template.events.on("over", function (ev) {
        var seriesType = im.getTargetSeriesType(ev.target.dataItem.dataContext);
        var target = "";

        // bring this series to front
        ev.target.dataItem.dataContext.zIndex = Number.MAX_VALUE;
        ev.target.dataItem.dataContext.toFront();

        if (seriesType === "MapImageSeries") {
          target = ev.target.dataItem.dataContext.mapImages;
          target.each(function (marker) {
            marker.setState("highlight");
          });
        } else if (seriesType === "MapPolygonSeries") {
          target = ev.target.dataItem.dataContext.mapPolygons;
          target.each(function (polygon) {
            if (!polygon.dataItem.dataContext.madeFromGeoData) {
              polygon.setState("highlight");
            }
          });
        } else {
          return;
        }
      });
      map.legend.itemContainers.template.events.on("out", function (ev) {
        var seriesType = im.getTargetSeriesType(ev.target.dataItem.dataContext);
        var target = "";

        if (seriesType === "MapImageSeries") {
          target = ev.target.dataItem.dataContext.mapImages;
          target.each(function (marker) {
            marker.setState("default");
          });
        } else if (seriesType === "MapPolygonSeries") {
          target = ev.target.dataItem.dataContext.mapPolygons;
          target.each(function (polygon) {
            if (!polygon.dataItem.dataContext.madeFromGeoData) {
              polygon.setState("default");
            }
          });
        } else {
          return;
        }
      });
    }
  }

  // Custom Legend
  if (data.customLegend && im.bool(data.customLegend.enabled)) {
    var customLegendType =
      typeof data.customLegend.type !== "undefined"
        ? data.customLegend.type
        : "internal";

    if (
      data.customLegend.data &&
      Array.isArray(data.customLegend.data) &&
      customLegendType === "internal"
    ) {
      customLegend = new am4maps.Legend();
      customLegend.parent = map.chartContainer;
      customLegend.data = data.customLegend.data;

      /*
			customLegend.data.map(function(entry){
			  entry.name = entry.name.replace(/&#(\d+);/g, function(match, dec) {
			    return String.fromCharCode(dec);
			  });
			  return entry;
			});*/

      if (
        typeof data.customLegend.style !== "undefined" &&
        data.customLegend.style !== "default"
      ) {
        im.setLegendStyle(id, customLegend, data.customLegend.style);
      } // no interaction with mouse

      customLegend.itemContainers.template.clickable = false;
      customLegend.itemContainers.template.focusable = false;
      customLegend.itemContainers.template.hoverable = false;
      customLegend.itemContainers.template.cursorOverStyle =
        am4core.MouseCursorStyle["default"];
      customLegend.labels.template.truncate = false;
      customLegend.labels.template.wrap = true; // positioing

      if (
        data.customLegend.position === "top" ||
        data.customLegend.position === "bottom"
      ) {
        customLegend.contentAlign = data.customLegend.align;
        customLegend.valign = data.customLegend.position;
      } else {
        customLegend.position = data.customLegend.position;
        customLegend.align = data.customLegend.position;
        customLegend.valign = data.customLegend.valign;
      }

      if (_typeof(data.customLegend.fill) !== undefined) {
        customLegend.labels.template.fill = data.customLegend.fill;
      }
    }
  }

  // Create Main Series
  regionSeries = im.pushRegionSeries(id, data);
  im.maps[id].baseRegionSeries = regionSeries; // Check for grouped regions

  if (Array.isArray(data.regionGroups) && data.regionGroups.length) {
    groupedSeries = im.pushGroupSeries(id, data);
    im.maps[id].groupedBaseRegionSeries = groupedSeries;
  } // Overlay collections - we can add all series in the preset order

  if (Array.isArray(data.overlay) && data.overlay.length) {
    data.overlay.forEach(function (mapObj) {
      im.pushSeries(id, mapObj);
    });
  } // Create Other Series - we create them after the overlay to avoid overlap

  if (Array.isArray(data.lines) && data.lines.length) {
    lineSeries = im.pushLineSeries(id, data);
  }

  if (Array.isArray(data.roundMarkers) && data.roundMarkers.length) {
    markerSeries = im.pushRoundMarkerSeries(id, data);

    if (data.clusterMarkers && im.bool(data.clusterMarkers.enabled)) {
      markerSeries.hidden = true;
      clusters = im.setupClusters(data, id);
      clusterSeries[id].zoomLevels[data.clusterMarkers.zoomLevel] =
        markerSeries;
      // we setup the main index series (zoom=1) to be visible
      // when doing it inside setupClusters function, there was a bug
      if (clusterSeries[id].zoomLevels[1]) {
        clusterSeries[id].zoomLevels[1].hidden = false;
        // we also add it to the base series here, since it wasn't working in the setupClusters function
        im.maps[id].allBaseSeries.push(clusterSeries[id].zoomLevels[1]);
      }
    }
  }

  if (Array.isArray(data.imageMarkers) && data.imageMarkers.length) {
    imageSeries = im.pushImageMarkerSeries(id, data); //imageSeries.hiddenInLegend = true;

    im.maps[id].allBaseSeries.push(imageSeries);
  }

  if (Array.isArray(data.iconMarkers) && data.iconMarkers.length) {
    iconSeries = im.pushIconMarkerSeries(id, data); //iconSeries.hiddenInLegend = true;

    im.maps[id].allBaseSeries.push(iconSeries);
  }

  if (Array.isArray(data.labels) && data.labels.length) {
    labelSeries = im.pushLabelSeries(id, data); //labelSeries.hiddenInLegend = true;

    im.maps[id].allBaseSeries.push(labelSeries);
  } // checks if we should display info and creates events.

  im.handleInfoBox(id); // map.projection = new am4maps.projections[data.projection]();
};

iMapsManager.setLegendStyle = function (id, legend, style) {
  var sizes = {
    xsmall: 10,
    small: 13,
    regular: 16,
    large: 19,
    larger: 23,
  };
  var legendSize = sizes[style];
  var legendMarkerTemplate = legend.markers.template;
  legend.labels.template.fontSize = legendSize;
  legend.useDefaultMarker = true;
  legendMarkerTemplate.width = legendSize;
  legendMarkerTemplate.height = legendSize;
};

iMapsManager.handleZoom = function (id) {
  var im = this,
    map = im.maps[id].map,
    data = im.maps[id].data,
    allCurrentSeries = im.maps[id].series,
    allBaseSeries = im.maps[id].allBaseSeries,
    defaultOffset = true,
    defaultZoom = true; // Viewport settings

  // Zoom Level
  if (
    typeof data.viewport !== "undefined" &&
    parseFloat(data.viewport.zoomLevel) >= 1
  ) {
    map.homeZoomLevel = parseFloat(data.viewport.zoomLevel);
    defaultZoom = false; // to make sure everything else is disabled by default

    map.seriesContainer.resizable = false;
    map.seriesContainer.draggable = false;
    map.chartContainer.wheelable = false;
  }

  // Home center point
  if (
    typeof data.viewport !== "undefined" &&
    data.viewport.homeGeoPoint &&
    data.viewport.homeGeoPoint.latitude !== 0 &&
    data.viewport.homeGeoPoint.longitude !== 0
  ) {
    map.homeGeoPoint = data.viewport.homeGeoPoint;
  }

  // delta Coordinates Offset
  if (typeof data.viewport !== "undefined" && data.viewport.offset) {
    // only change if there are values, otherwise we might messup projections (Albers)
    if (
      data.viewport.offset.longitude !== "" &&
      data.viewport.offset.longitude !== "0"
    ) {
      defaultOffset = false;
      map.deltaLongitude = data.viewport.offset.longitude;
    }

    if (
      data.viewport.offset.latitude !== "" &&
      data.viewport.offset.latitude !== "0"
    ) {
      defaultOffset = false;
      map.deltaLatitude = data.viewport.offset.latitude;
    }

    if (defaultOffset) {
      iMapsManager.latlongOffsetFix(data, map, defaultZoom);
    }
  }

  // manual fixes for NZ and RU and Asia maps
  if (typeof data.viewport === "undefined") {
    iMapsManager.latlongOffsetFix(data, map, defaultZoom);
  }

  // default zoom behaviour
  if (typeof data.zoom === "undefined") {
    // default zoom behaviour when we can't find zoom settings
    if (typeof data.zoomMaster !== "undefined" && im.bool(data.zoomMaster)) {
      map.seriesContainer.draggable = true;
      map.seriesContainer.resizable = true;

      // display zoom controls by default
      map.zoomControl = new am4maps.ZoomControl();
      map.zoomControl.focusable = false;

      // iOS fix
      map.zoomControl.exportable = false;
      if (map.zoomControl.children && map.zoomControl.children.values) {
        map.zoomControl.children.values.forEach(function (child) {
          child.focusable = false;
        });
      }

      map.zoomControl.align = "right";
      map.zoomControl.valign = "bottom";
    } else {
      map.seriesContainer.resizable = false;
      map.seriesContainer.draggable = false;
    }

    map.seriesContainer.events.disableType("doublehit");
    map.chartContainer.background.events.disableType("doublehit");
    map.chartContainer.wheelable = false;
    return;
  } // disable drag in Ortographic and leave default for the rest

  if (data.projection !== "Orthographic") {
    map.seriesContainer.draggable = data.zoom
      ? im.bool(data.zoom.draggable)
      : false;
    map.seriesContainer.resizable = data.zoom
      ? im.bool(data.zoom.draggable)
      : false; // don't zoom out to center

    if (im.bool(data.zoom.draggable)) {
      // in case we want to show the grab hand on mousehover also
      //map.seriesContainer.cursorOverStyle = am4core.MouseCursorStyle.grab;
      map.seriesContainer.cursorDownStyle = am4core.MouseCursorStyle.grabbing;
    }

    // control zoom and pan behaviour
    map.centerMapOnZoomOut = false;
    map.maxPanOut = 0;

    // zoom is enabled, only allowdrag on mobile
    if (
      im.bool(data.zoom.enabled) &&
      !im.bool(data.zoom.draggable) &&
      im.bool(data.zoom.mobileResizable) &&
      /Mobi|Android/i.test(navigator.userAgent)
    ) {
      map.seriesContainer.draggable = true;
      map.seriesContainer.resizable = true;
    }
  } else {
    map.seriesContainer.draggable = false;
    map.seriesContainer.resizable = false;
    map.panBehavior = "rotateLongLat";
  } // disable zoom

  if (!im.bool(data.zoom.enabled)) {
    map.maxZoomLevel = parseFloat(data.viewport.zoomLevel);
    map.seriesContainer.events.disableType("doublehit");
    map.chartContainer.background.events.disableType("doublehit");
    map.seriesContainer.draggable = false;
    map.seriesContainer.resizable = false;
    map.chartContainer.wheelable = false;
  } else {
    // mouse wheel zoom
    map.chartContainer.wheelable = im.bool(data.zoom.wheelable); // double click zoom

    if (!im.bool(data.zoom.doubleHitZoom)) {
      map.seriesContainer.events.disableType("doublehit");
      map.chartContainer.background.events.disableType("doublehit");
    }

    if (typeof data.zoom.maxZoomLevel !== "undefined") {
      map.maxZoomLevel = parseInt(data.zoom.maxZoomLevel);
    } // Zoom Controls

    if (im.bool(data.zoom.controls)) {
      map.zoomControl = new am4maps.ZoomControl();

      // iOS fix
      map.zoomControl.exportable = false;
      if (map.zoomControl.children && map.zoomControl.children.values) {
        map.zoomControl.children.values.forEach(function (child) {
          child.focusable = false;
        });
      }

      map.zoomControl.exportable = false;
      map.zoomControl.align = data.zoom.controlsPositions
        ? data.zoom.controlsPositions.align
        : "right";
      map.zoomControl.valign = data.zoom.controlsPositions
        ? data.zoom.controlsPositions.valign
        : "bottom";

      if (
        typeof data.zoom.homeButton === "undefined" ||
        im.bool(data.zoom.homeButton)
      ) {
        // home button
        var homeButton = new am4core.Button();
        // iOS scroll fix
        homeButton.focusable = false;
        homeButton.events.on("hit", function () {
          map.goHome();
          // in case drillDown is enabled, we hide everything else
          iMapsManager.resetDrilldown(id);
          // reset actions
          if (
            typeof iMapsActions !== "undefined" &&
            typeof iMapsActions.resetActions !== "undefined"
          ) {
            iMapsActions.resetActions(id);
          }
        });
        homeButton.icon = new am4core.Sprite();
        homeButton.padding(7, 5, 7, 5);
        homeButton.width = 30;
        homeButton.icon.path =
          "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";
        homeButton.marginBottom = 3;
        homeButton.parent = map.zoomControl;
        homeButton.insertBefore(map.zoomControl.plusButton); // fix for Firefox homebutton not being loaded correctly

        map.events.on("inited", function () {
          homeButton.deepInvalidate();
        });
      }
    } // outside zoom controls

    if (im.bool(data.zoom.externalControls)) {
      // add home button
      iMapsManager.handleExternalZoom(data.id);
    }
  } // full screen button

  if (
    typeof data.fullScreen !== "undefined" &&
    im.bool(data.fullScreen.enabled)
  ) {
    // full screen
    var fullScreenButton = map.chartContainer.createChild(am4core.Button);
    fullScreenButton.events.on("hit", function (ev) {
      var parentMap = document.querySelector("#map_" + ev.target.icon.mapID);
      var mainParent = parentMap.closest(".map_wrapper");

      // browser fullscreen
      iMapsManager.toggleFullscreen(mainParent);
      iMapsManager.isFullScreen = ev.target;
    });

    // Add button
    fullScreenButton.align = data.fullScreen.align;
    fullScreenButton.valign = data.fullScreen.valign;
    fullScreenButton.margin(5, 5, 5, 5);
    fullScreenButton.padding(5, 0, 5, 0);
    fullScreenButton.width = 30;
    fullScreenButton.icon = new am4core.Sprite();
    fullScreenButton.icon.path = iMapsManager.library.icons.goFullIconPath;
    fullScreenButton.icon.isFullScreen = false;
    fullScreenButton.icon.mapID = id; // if mobile only, add class

    if (im.bool(data.fullScreen.mobileOnly)) {
      fullScreenButton.id = "_fullscreen_button_only_mobile";
    } else {
      fullScreenButton.id = "_fullscreen_button";
    }

    // Solution for Firefox issue with button container
    map.events.on("inited", function () {
      fullScreenButton.deepInvalidate();
    });
  }

  // pan events?
  map.events.on("mappositionchanged", function (ev) {
    // what to do here to have tooltips always display follow pan?
  });

  // zoom level changed events
  map.events.on("zoomlevelchanged", function (ev) {
    var clusterSeries = im.maps[id].clusterSeries,
      closest,
      zlevel = ev.target.zoomLevel,
      isDrill = im.bool(data.drillDownOnClick),
      // Check if data.liveFilter exists and has the property 'enabled' before accessing it
      isDrilling = im.maps[id].isDrilling,
      drilledTo = im.maps[id].drilledTo,
      activeMap = im.filteredMap,
      keepBase = false;

      var hasLiveFilter = false;
      if(typeof data.liveFilter !== "undefined") {
        var hasLiveFilter = im.bool(data.liveFilter.enabled);
      }

    // for drilldown
    if (
      isDrill &&
      typeof data.alwaysKeepBase !== "undefined" &&
      im.bool(data.alwaysKeepBase)
    ) {
      keepBase = true;
    }

    // for live filter
    // Added check to ensure data.liveFilter exists before checking its 'keepBase' property
    if (hasLiveFilter && data.liveFilter && im.bool(data.liveFilter.keepBase)) {
      keepBase = true;
    }

    // where the magic happens to show/hide series from cluster markers
    if (clusterSeries && Object.keys(clusterSeries).length) {
      Object.keys(clusterSeries).forEach(function (key) {
        // if the drilldown or live filter are enabled
        if (isDrill || hasLiveFilter) {
          if (
            //active map in live filter is the map the cluster belongs to
            (hasLiveFilter &&
              parseInt(activeMap) === parseInt(clusterSeries[key].overlay)) ||
            // current active map is the base map in the filter
            (hasLiveFilter && parseInt(activeMap) === parseInt(id)) ||
            // there's a filter, but the keepBase is enabled, so clusters that are not overlay still display
            (hasLiveFilter &&
              keepBase &&
              !im.bool(clusterSeries[key].overlay)) ||
            //map being drilled to is the map the cluster belongs to
            (isDrilling &&
              drilledTo &&
              parseInt(drilledTo) === parseInt(clusterSeries[key].overlay)) ||
            //drilldown is enabled and cluster is from the base map and we are not drilling
            (isDrill &&
              !im.bool(clusterSeries[key].overlay) &&
              !keepBase &&
              !drilledTo) ||
            //drilldown is enabled, cluster is from base map and keepBase is enabled
            (isDrill && !im.bool(clusterSeries[key].overlay) && keepBase)
          ) {
            // first we get the closest zoom value compared to the current zom
            closest = im.getClosest(clusterSeries[key].zoomLevels, zlevel);
            Object.keys(clusterSeries[key].zoomLevels).forEach(function (zkey) {
              clusterSeries[key].zoomLevels[zkey].hide();
              if (parseFloat(zkey) === closest) {
                clusterSeries[key].zoomLevels[zkey].show();
              } else {
                clusterSeries[key].zoomLevels[zkey].hide();
              }
            });
          } else {
            // not drilling and not active map
            Object.keys(clusterSeries[key].zoomLevels).forEach(function (zkey) {
              clusterSeries[key].zoomLevels[zkey].hide();
            });
          }

          return;
        }

        // it's not a drilldown, proceed
        // we go through the series to show the closest one and hide the others
        closest = im.getClosest(clusterSeries[key].zoomLevels, zlevel);
        Object.keys(clusterSeries[key].zoomLevels).forEach(function (zkey) {
          // we hide them by default
          clusterSeries[key].zoomLevels[zkey].hide();

          if (parseFloat(zkey) === closest) {
            clusterSeries[key].zoomLevels[zkey].show();
          } else {
            clusterSeries[key].zoomLevels[zkey].hide();
          }
        });
      });
    }
  });
};
/**
 * Gets closest zoom level based on current zoom and array of zoom levels for clusters
 * @param {*} zoomLevels
 * @param {*} zlevel
 */

iMapsManager.getClosest = function (zoomLevels, zlevel) {
  var closest = Object.keys(zoomLevels).reduce(function (prev, curr) {
    prev = parseInt(prev);
    curr = parseInt(curr);
    return Math.abs(curr - zlevel) < Math.abs(prev - zlevel) ? curr : prev;
  });
  return closest;
};

/* Reset drilldown behaviour */
iMapsManager.resetDrilldown = function (id) {
  var im = this,
    data = im.maps[id].data,
    allCurrentSeries = im.maps[id].series,
    allBaseSeries = im.maps[id].allBaseSeries,
    bgSeries = im.maps[id].backgroundSeries;

  if (im.bool(data.drillDownOnClick)) {
    for (var i = 0, len = allCurrentSeries.length; i < len; i++) {
      allCurrentSeries[i].hide(); //map.deltaLongitude = 0;
    }

    for (var ib = 0, lenb = allBaseSeries.length; ib < lenb; ib++) {
      // this is messing the cluster markers on base map
      allBaseSeries[ib].show();

      // make sure there's a bg series
      if (bgSeries.mapPolygons) {
        bgSeries.show();
      }
    }

    iMapsManager.maps[id].drilledTo = false;
    iMapsManager.maps[id].isDrilling = false;
  }
};

/** Manually fix lat/long offset for some countries in default projections/values */

iMapsManager.latlongOffsetFix = function (data, mapObj, defaultZoom) {
  var mapSelected = data.map;
  var mapsFixInclude = [
    "russiaLow",
    "russiaHigh",
    "russiaCrimeaLow",
    "russiaCrimeaHigh",
    "region/world/asiaLow",
    "region/world/asiaHigh",
    "region/world/asiaUltra",
    "region/world/asiaIndiaLow",
    "region/world/asiaIndiaHigh",
    "region/world/asiaIndiaUltra",
  ];

  // only do the fix if Russia is included

  if (
    mapsFixInclude.includes(mapSelected) &&
    data.exclude &&
    !data.exclude.includes("RU")
  ) {
    mapObj.deltaLongitude = -100;

    if (defaultZoom) {
      mapObj.homeZoomLevel = 1.5;
    }
  }

  if (mapSelected === "newZealandLow" || mapSelected == "newZealandHigh") {
    mapObj.deltaLongitude = 20;
  }
};
/**
 * Push region series that are grouped together
 */

iMapsManager.pushGroupSeries = function (id, data) {
  var series = [];
  var regionSerie;
  data.regionGroups.forEach(function (group) {
    var newData = {},
      include = group.map(function (a) {
        return a.id;
      });

    // let's get all the options from the main map and change the group option to true
    newData = Object.assign({}, data);
    newData.regionsGroupHover = true;
    newData.regions = group;
    newData.include = include; // include only the regions we're grouping
    regionSerie = iMapsManager.pushRegionSeries(id, newData, true);
    series.push(regionSerie);
  });
  return series;
};
/**
 * Push different series of overlay/child maps
 * int i - index of the map data
 * data - object with map data to push
 */

iMapsManager.pushSeries = function (id, data) {
  var im = this,
    regionSeries,
    markerSeries,
    labelSeries,
    lineSeries,
    iconSeries,
    imageSeries,
    groupedSeries,
    clusters,
    clusterSeries = im.maps[id].clusterSeries,
    parentData = im.maps[id].data,
    seriesIndex = im.maps[id].seriesIndex,
    seriesById = im.maps[id].seriesById,
    isDrill = im.bool(im.maps[id].data.drillDownOnClick),
    cleanMapName = iMapsRouter.getCleanMapName(data.map, data.id),
    defaultSeries = false;

  if (false === cleanMapName) {
    return;
  }

  if (typeof data.id === "undefined") {
    return;
  }

  seriesById[data.id] = [];

  // check if it's set a map overlay by default
  if (
    typeof parentData.liveFilter !== "undefined" &&
    parseInt(parentData.liveFilter["default"]) !== parentData.id
  ) {
    defaultSeries = parseInt(parentData.liveFilter["default"]);
    im.filteredMap = defaultSeries;
  } // setup series index

  if (!Array.isArray(im.maps[id].seriesIndex[data.map])) {
    im.maps[id].seriesIndex[cleanMapName] = [];
  }

  // to allow empty maps to overlay, we removed the check && data.regions.length and send empty array
  if (!Array.isArray(data.regions)) {
    data.regions = [];
  }

  if (typeof parentData.allowEmpty === "undefined") {
    parentData.allowEmpty = 0;
  } // in case we want empty maps to overlay, we remove this check.
  // in are not allowing the emtpy so that the live filter works better and markers are not hidden behind the map
  // reference: https://interactivegeomaps.com/feature/live-filter/
  // but other overlays and empty maps might need to be added...

  // we don't check if the regions.lenght exist, because maybe user wants to add an empty map, only to show divisions
  if (data.regions.length || im.bool(parentData.allowEmpty)) {
    // in case we don't allow empty, we only include the active regions
    if (!im.bool(parentData.allowEmpty)) {
      data.include = [];
      data.regions.forEach(function (region, index) {
        data.include.push(region.id);
        if (!isNaN(region.id)) {
          data.include.push(parseInt(region.id));
        }
      });
    }

    regionSeries = iMapsManager.pushRegionSeries(id, data);
    seriesIndex[cleanMapName].push(regionSeries);
    seriesById[data.id].push(regionSeries);

    if (isDrill) {
      regionSeries.hidden = true;
      regionSeries.mapID = data.id;
    }

    // hide in case we have a live filter and this is not the default
    if (
      parentData.liveFilter &&
      im.bool(parentData.liveFilter.enabled) &&
      defaultSeries &&
      defaultSeries !== data.id
    ) {
      regionSeries.hidden = true;
    }
  }

  // Check for grouped regions
  if (Array.isArray(data.regionGroups) && data.regionGroups.length) {
    groupedSeries = im.pushGroupSeries(id, data);
    groupedSeries.forEach(function (regionSerie) {
      seriesIndex[cleanMapName].push(regionSerie);
      seriesById[data.id].push(regionSerie);

      if (isDrill) {
        regionSerie.hidden = true;
        regionSerie.mapID = data.id;
      }

      // hide in case we have a live filter and this is not the default
      if (
        parentData.liveFilter &&
        im.bool(parentData.liveFilter.enabled) &&
        defaultSeries &&
        defaultSeries !== data.id
      ) {
        regionSerie.hidden = true;
      }
    });
  } // let's add lines before markers, so that the markers then overlay the end of lines, in case

  if (Array.isArray(data.lines) && data.lines.length) {
    lineSeries = iMapsManager.pushLineSeries(id, data);
    seriesIndex[cleanMapName].push(lineSeries);
    seriesById[data.id].push(lineSeries);

    if (isDrill) {
      lineSeries.hidden = true;
    } // hide in case we have a live filter and this is not the default

    if (
      parentData.liveFilter &&
      im.bool(parentData.liveFilter.enabled) &&
      defaultSeries &&
      defaultSeries !== data.id
    ) {
      lineSeries.hidden = true;
    }
  }

  if (Array.isArray(data.roundMarkers) && data.roundMarkers.length) {
    markerSeries = iMapsManager.pushRoundMarkerSeries(id, data);
    seriesIndex[cleanMapName].push(markerSeries);
    seriesById[data.id].push(markerSeries);

    if (isDrill) {
      markerSeries.hidden = true;
      markerSeries.mapID = data.id;
    }

    // clusters in overlay maps
    if (data.clusterMarkers && im.bool(data.clusterMarkers.enabled)) {
      markerSeries.hidden = true;
      clusters = im.setupClusters(data, id, data.id);
      clusterSeries[data.id].zoomLevels[data.clusterMarkers.zoomLevel] =
        markerSeries; // we setup the main index series (zoom=1) to be visible
      // when doing it inside setupClusters function, there was a bug

      if (!isDrill && clusterSeries[data.id].zoomLevels[1]) {
        clusterSeries[data.id].zoomLevels[1].hidden = false;

        // if there's a live filter, double check - we hidde them.
        // otherwise overlaid series with clusters were always displaying, even when they shouldn't
        if (
          parentData.liveFilter &&
          im.bool(parentData.liveFilter.enabled) &&
          defaultSeries &&
          defaultSeries !== data.id
        ) {
          clusterSeries[data.id].zoomLevels[1].hidden = true;
        }
      }
    }

    // hide in case we have a live filter and this is not the default
    if (
      parentData.liveFilter &&
      im.bool(parentData.liveFilter.enabled) &&
      defaultSeries &&
      defaultSeries !== data.id
    ) {
      markerSeries.hidden = true;
    }
  }

  if (Array.isArray(data.iconMarkers) && data.iconMarkers.length) {
    iconSeries = iMapsManager.pushIconMarkerSeries(id, data);
    seriesIndex[cleanMapName].push(iconSeries);
    seriesById[data.id].push(iconSeries);

    if (isDrill) {
      iconSeries.hidden = true;
    } // hide in case we have a live filter and this is not the default

    if (
      parentData.liveFilter &&
      im.bool(parentData.liveFilter.enabled) &&
      defaultSeries &&
      defaultSeries !== data.id
    ) {
      iconSeries.hidden = true;
    }
  }

  if (Array.isArray(data.imageMarkers) && data.imageMarkers.length) {
    imageSeries = iMapsManager.pushImageMarkerSeries(id, data);
    seriesIndex[cleanMapName].push(imageSeries);
    seriesById[data.id].push(imageSeries);

    if (isDrill) {
      imageSeries.hidden = true;
    } // hide in case we have a live filter and this is not the default

    if (
      parentData.liveFilter &&
      im.bool(parentData.liveFilter.enabled) &&
      defaultSeries &&
      defaultSeries !== data.id
    ) {
      imageSeries.hidden = true;
    }
  }

  if (Array.isArray(data.labels) && data.labels.length) {
    labelSeries = iMapsManager.pushLabelSeries(id, data);
    seriesIndex[cleanMapName].push(labelSeries);
    seriesById[data.id].push(labelSeries);

    if (isDrill) {
      labelSeries.hidden = true;
    } // hide in case we have a live filter and this is not the default

    if (
      parentData.liveFilter &&
      im.bool(parentData.liveFilter.enabled) &&
      defaultSeries &&
      defaultSeries !== data.id
    ) {
      labelSeries.hidden = true;
    }
  }
};

iMapsManager.pushRegionSeries = function (id, data, groupHover) {
  var im = this,
    map = im.maps[id].map,
    // shorter reference for the map
    regionSeries,
    tooltipConfig = data.tooltip || {},
    regionTemplate,
    hover,
    active,
    highlight,
    mapVar,
    clkLabel,
    groupHover = groupHover || false;
  data = data || {};
  regionSeries = map.series.push(new am4maps.MapPolygonSeries());

  if (data.map === "custom" || im.bool(data.useGeojson)) {
    regionSeries.geodataSource.url = data.mapURL;
  } else {
    mapVar = iMapsRouter.getVarByName(data.map);
    regionSeries.geodata = window[mapVar];
  }

  regionSeries.name =
    data.regionLegend && data.regionLegend.title !== ""
      ? data.regionLegend.title
      : data.title;
  regionSeries.hiddenInLegend = data.regionLegend
    ? !im.bool(data.regionLegend.enabled)
    : true; // if it's a base series

  // override hide in legend for scenarios where there are grouped entries for example
  if (
    data.regionLegend &&
    data.regionLegend.enabled === "onlyGroups" &&
    groupHover
  ) {
    regionSeries.hiddenInLegend = false;
  }

  if (
    data.regionLegend &&
    data.regionLegend.enabled === "onlyGroups" &&
    !groupHover
  ) {
    regionSeries.hiddenInLegend = true;
  }

  if (
    data.regionLegend &&
    data.regionLegend.enabled === "ignoreGroups" &&
    groupHover
  ) {
    regionSeries.hiddenInLegend = true;
  }

  if (
    data.regionLegend &&
    data.regionLegend.enabled === "ignoreGroups" &&
    !groupHover
  ) {
    regionSeries.hiddenInLegend = false;
  }

  if (id === data.id) {
    // add it as the baseSeries - which will contain all region base series
    if (typeof im.maps[id].baseSeries === "undefined") {
      im.maps[id].baseSeries = [];
    }
    im.maps[id].baseSeries.push(regionSeries);
    im.maps[id].allBaseSeries.push(regionSeries);
  }

  // Make map load polygon (like country names) data from GeoJSON
  regionSeries.useGeodata = true; // Exclude

  if (Array.isArray(data.exclude) && data.exclude.length) {
    regionSeries.exclude = data.exclude;
  } // Include

  if (Array.isArray(data.include) && data.include.length) {
    regionSeries.include = data.include;
  } // Setup Heatmap rules

  if (data.heatMapRegions && im.bool(data.heatMapRegions.enabled)) {
    im.setupHeatMap(regionSeries, id, data);
  } // Data
  // if (Array.isArray(data.regions)) {

  regionSeries.data = data.regions; // Configure series

  if (groupHover) {
    regionSeries.groupHover = true;
  }

  regionTemplate = regionSeries.mapPolygons.template;
  im.setupTooltip(id, regionSeries, data); // check for custom tooltip template

  if (
    typeof data.regionsTooltipTemplate !== "undefined" &&
    data.regionsTooltipTemplate.trim() !== ""
  ) {
    regionTemplate.tooltipText = data.regionsTooltipTemplate;
    regionTemplate.tooltipHTML = data.regionsTooltipTemplate;
  } else {
    regionTemplate.tooltipText = tooltipConfig.template
      ? tooltipConfig.template
      : "{tooltipContent}";
    regionTemplate.tooltipHTML = tooltipConfig.template
      ? tooltipConfig.template
      : "{tooltipContent}";
  }

  regionTemplate.adapter.add("tooltipHTML", function (value, target, key) {
    if (
      _typeof(target.dataItem.dataContext) === "object" &&
      typeof tooltipConfig.onlyWithData !== "undefined"
    ) {
      // check if we don't return tooltip on empty regions
      if (im.bool(tooltipConfig.onlyWithData)) {
        if (target.dataItem.dataContext.madeFromGeoData === true) {
          target.tooltip.tooltipText = undefined;
          target.tooltip.tooltipHTML = undefined;
          return "";
        }
      }
    }

    if (value === "") {
      return value;
    }

    return value.replace(/\\/g, "");
  });
  regionTemplate.adapter.add("tooltipText", function (value, target, key) {
    if (
      _typeof(target.dataItem.dataContext) === "object" &&
      typeof tooltipConfig.onlyWithData !== "undefined"
    ) {
      // check if we don't return tooltip on empty regions
      if (im.bool(tooltipConfig.onlyWithData)) {
        if (target.dataItem.dataContext.madeFromGeoData === true) {
          target.tooltip.tooltipText = undefined;
          target.tooltip.tooltipHTML = undefined;
          return "";
        }
      }
    }

    if (value === "") {
      return value;
    }

    return value.replace(/\\/g, "");
  }); // For legend color

  regionSeries.fill = data.regionDefaults.fill;
  regionTemplate.fill = data.regionDefaults.inactiveColor;
  regionTemplate.stroke = data.visual.borderColor;
  regionTemplate.strokeWidth = data.visual.borderWidth; // fill

  regionTemplate.propertyFields.fill = "fill";

  // exploring adapter
  /*
		regionTemplate.adapter.add("fill", function(fill, target) {
			if(Array.isArray(fill)){
	      let gradient = new am4core.LinearGradient();
	      fill.forEach(function(color){
	        gradient.addColor(am4core.color(color));
	      });
	      fill = gradient;
	    }
	    return fill;
		});
	  */

  // hover - only create if it's not a group hover series

  if (!groupHover) {
    // also don't consider it on heatmaps with hover disabled
    if (
      typeof data.heatMapRegions !== "undefined" &&
      im.bool(data.heatMapRegions.enabled) &&
      im.bool(data.heatMapRegions.noHover)
    ) {
      // do nothing for now, we don't need hover, might implement something at some point
    } else {
      hover = regionTemplate.states.create("hover");
      hover.propertyFields.fill = "hover";
    }
    //hover.propertyFields.strokeWidth = "borderWidthHover";
    //hover.propertyFields.stroke = "borderColorHover";
  }

  // active state
  if (data.regionActiveState && im.bool(data.regionActiveState.enabled)) {
    active = regionTemplate.states.create("active");

    if (data.regionActiveState.source === "custom") {
      active.properties.fill = data.regionActiveState.fill;
    } else {
      active.propertyFields.fill = "hover";
    }
  }
  // highlight - for group hover
  highlight = regionTemplate.states.create("highlight");
  highlight.propertyFields.fill = "hover";

  if (groupHover) {
    regionTemplate.events.on("out", function (ev) {
      im.groupHoverOut(id, ev);
    });
    regionTemplate.events.on("over", function (ev) {
      im.groupHover(id, ev);
      // bring this series (and label series if it exists) to front when group is hovered
      regionSeries.toFront();
      if (typeof regionSeries.autoLabelSeries !== "undefined") {
        regionSeries.autoLabelSeries.toFront();
      }
    });
    regionTemplate.events.on("hit", function (ev) {
      im.groupHit(id, ev);
    });
  } else {
    regionTemplate.events.on("hit", function (ev) {
      im.singleHit(id, ev);
    });
    regionTemplate.events.on("over", function (ev) {
      im.setupHoverEvents(id, ev);
    });
  } // Events

  // iOS bug fix, remove focus
  regionTemplate.focusable = false;

  regionTemplate.events.on("hit", function (ev) {
    im.setupHitEvents(id, ev);
  });

  // enable small map
  if (im.bool(data.smallMap)) {
    map.smallMap.series.push(regionSeries);
  }

  // auto Labels
  if (data.regionLabels && im.bool(data.regionLabels.source)) {
    regionSeries.calculateVisualCenter = true;

    // Configure label series
    var labelSeries = map.series.push(new am4maps.MapImageSeries());
    var labelTemplate = labelSeries.mapImages.template.createChild(
      am4core.Label
    );

    // Label Background - currently only possible with global
    var background =
      typeof igmLabelsBackground !== "undefined" ? igmLabelsBackground : false;
    if (typeof background === "object") {
      labelTemplate.background = new am4core.RoundedRectangle();
      labelTemplate.background.cornerRadius(...background.cornerRadius);
      labelTemplate.background.fill = am4core.color(background.color);
      labelTemplate.padding(...background.padding);
      labelTemplate.background.stroke = am4core.color(background.stroke);
    }

    im.maps[id].labelSeries.push(labelSeries);

    // add reference to parent region series
    regionSeries.autoLabelSeries = labelSeries;

    labelTemplate.horizontalCenter = data.regionLabels.horizontalCenter;
    labelTemplate.verticalCenter = data.regionLabels.verticalCenter;
    labelTemplate.fontSize = data.regionLabels.fontSize;
    labelTemplate.fill = data.regionLabels.fill;

    labelTemplate.fontFamily = data.regionLabels.fontFamily
      ? data.regionLabels.fontFamily
      : "inherit";
    labelTemplate.fontWeight = data.regionLabels.fontWeight
      ? data.regionLabels.fontWeight
      : "normal";

    // let's set a listener to the hide event of main series to hide this one also
    if (
      data.regionLabels.mobileFontSize &&
      parseInt(data.regionLabels.mobileFontSize) !== 100
    ) {
      if (window.innerWidth <= 780) {
        labelTemplate.fontSize =
          (parseInt(labelTemplate.fontSize) *
            parseInt(data.regionLabels.mobileFontSize)) /
          100;
        labelTemplate.size = labelTemplate.fontSize;
      }
    }

    regionSeries.events.on("hidden", function (ev) {
      labelSeries.hide();
    });
    regionSeries.events.on("shown", function (ev) {
      labelSeries.show();
    });
    // label events
    labelTemplate.events.on("hit", function (ev) {
      clkLabel = regionSeries.getPolygonById(ev.target.parent.LabelForRegion);
      clkLabel.dispatchImmediately("hit");
    });
    labelTemplate.events.on("over", function (ev) {
      iMapsManager.hover(id, ev.target.parent.LabelForRegion, false);
    });
    labelTemplate.events.on("out", function (ev) {
      iMapsManager.clearHovered(id, ev.target.parent.LabelForRegion);
    });

    im.setupTooltip(id, labelSeries, data, labelTemplate);
    labelTemplate.interactionsEnabled = true;
    labelTemplate.nonScaling = im.bool(data.regionLabels.nonScaling);
    labelSeries.hiddenInLegend = true; // fix initially hidden series - for example drilldown overlay

    regionSeries.events.on("inited", function () {
      if (regionSeries.hidden) {
        labelSeries.hide();
        labelSeries.hidden = true;
      }
    });

    // set labels drag listener
    // allow labels to be dragged if in admin

    if (im.bool(data.admin)) {
      labelTemplate.draggable = true;
      labelTemplate.cursorOverStyle = am4core.MouseCursorStyle.grab;
      labelTemplate.events.on("dragstop", function (ev) {
        var svgPoint = am4core.utils.spritePointToSvg(
          {
            x: 0,
            y: 0,
          },
          ev.target
        );
        svgPoint = am4core.utils.spritePointToSvg(
          {
            x: 0 - ev.target.maxLeft,
            y: 0 - ev.target.maxTop,
          },
          ev.target
        );
        var geo = map.svgPointToGeo(svgPoint); // check if field to add json object with adjustments exists

        var adjField = document.querySelector(
          "[data-depend-id=" + data.regionLabelCustomCoordinates + "]"
        );

        if (adjField) {
          var jsonAdjustments;

          if (iMapsManager.isJSON(adjField.value)) {
            jsonAdjustments = JSON.parse(adjField.value);
          } else {
            jsonAdjustments = {};
          }

          jsonAdjustments[ev.target.parent.LabelForRegion] = {
            latitude: geo.latitude,
            longitude: geo.longitude,
          };
          adjField.value = JSON.stringify(jsonAdjustments);
        }

        map.seriesContainer.draggable = im.bool(data.zoom.draggable);
        ev.target.cursorOverStyle = am4core.MouseCursorStyle.grab;
      });
      labelTemplate.events.on("down", function (ev) {
        map.seriesContainer.draggable = false;
        ev.target.cursorOverStyle = am4core.MouseCursorStyle.grabbing;
      });
    } // end dragevent

    // convert custom json position string to object
    var regionLabelCustomCoordinates = im.isJSON(
      data.regionLabels.regionLabelCustomCoordinates
    )
      ? JSON.parse(data.regionLabels.regionLabelCustomCoordinates)
      : false;

    let event = "inited";
    if (data.map === "custom") {
      event = "datavalidated";
    }

    regionSeries.events.on(event, function () {
      regionSeries.mapPolygons.each(function (polygon) {
        // if they are not displaying with lat/long, skip, unless we're using a custom map
        if (
          data.map !== "custom" &&
          typeof polygon.visualLatitude === "undefined"
        ) {
          return;
        }

        var label = labelSeries.mapImages.create(),
          text; // if we're only adding labels to active regions

        if (
          im.bool(data.regionLabels.activeOnly) &&
          polygon.dataItem.dataContext &&
          typeof polygon.dataItem.dataContext.tooltipContent === "undefined"
        ) {
          return;
        } // if it's a group entry, ignore

        if (polygon.dataItem.dataContext.id.includes(",")) {
          return;
        }

        if (data.regionLabels.source === "code") {
          text = polygon.dataItem.dataContext.id.split("-").pop();
        }

        if (data.regionLabels.source === "{name}") {
          text = polygon.dataItem.dataContext.name;
        }

        if (data.regionLabels.source === "{id}") {
          text = polygon.dataItem.dataContext.id;
        }

        if (
          data.regionLabels.source === "custom" &&
          typeof data.regionLabels.customSource !== "undefined"
        ) {
          text = polygon.dataItem.dataContext.autoLabel;
        }

        label.LabelForRegion = polygon.dataItem.dataContext.id; // if it was a group

        if (typeof polygon.dataItem.dataContext.originalID !== "undefined") {
          label.groupRegion = polygon.dataItem.dataContext.originalID;
        }

        // tooltip content
        label.tooltipDataItem = polygon.tooltipDataItem;
        label.tooltip = polygon.tooltip;
        label.tooltipHTML = polygon.tooltipHTML;
        label.tooltipPosition = im.bool(data.tooltip.fixed)
          ? "fixed"
          : "pointer"; // cursor style

        if (
          polygon.dataItem.dataContext.action &&
          polygon.dataItem.dataContext.action !== "none"
        ) {
          label.cursorOverStyle = am4core.MouseCursorStyle.pointer;
        }

        // use custom coordinates adjustments or use auto position
        if (
          regionLabelCustomCoordinates &&
          regionLabelCustomCoordinates.hasOwnProperty(
            polygon.dataItem.dataContext.id
          )
        ) {
          label.latitude =
            regionLabelCustomCoordinates[
              polygon.dataItem.dataContext.id
            ].latitude;
          label.longitude =
            regionLabelCustomCoordinates[
              polygon.dataItem.dataContext.id
            ].longitude;
        } else {
          if (polygon.visualLatitude) {
            label.latitude = polygon.visualLatitude;
            label.longitude = polygon.visualLongitude;
          }
        }

        if (label.children.getIndex(0)) {
          label.children.getIndex(0).text = text;
        }
      });
    });
  }

  // if the external dropdown is enabled, calculate visual center

  if (
    typeof data.externalDropdown !== "undefined" &&
    im.bool(data.externalDropdown.enabled)
  ) {
    regionSeries.calculateVisualCenter = true;
  } // add this series to map series to reference it later if needed

  im.maps[id].series.push(regionSeries);
  return regionSeries;
};

iMapsManager.pushRoundMarkerSeries = function (id, data) {
  var im = this,
    map = im.maps[id].map,
    // shorter reference for the map
    markerSeries,
    markerSeriesTemplate,
    circle,
    label,
    activeState,
    highlightState,
    hoverState;

  if (Array.isArray(data.roundMarkers) && data.roundMarkers.length) {
    // Create image series
    markerSeries = map.series.push(new am4maps.MapImageSeries());
    markerSeries.name =
      data.roundMarkersLegend && data.roundMarkersLegend.title !== ""
        ? data.roundMarkersLegend.title
        : data.title;
    markerSeries.hiddenInLegend = data.roundMarkersLegend
      ? !im.bool(data.roundMarkersLegend.enabled)
      : false; // Create a circle image in image series template so it gets replicated to all new images

    markerSeriesTemplate = markerSeries.mapImages.template;

    circle = markerSeriesTemplate.createChild(am4core.Circle);
    im.setupTooltip(id, markerSeries, data, circle);

    //iOS focus scroll bug fix
    markerSeriesTemplate.focusable = false;

    circle.radius = data.markerDefaults.radius;
    circle.fill = data.markerDefaults.fill;
    circle.stroke = am4core.color("#FFFFFF");
    circle.strokeWidth = 1;
    circle.nonScaling =
      typeof igmRoundMarkersNonScaling !== "undefined"
        ? igmRoundMarkersNonScaling
        : true;

    // label
    label = markerSeriesTemplate.createChild(am4core.Label);
    label.text = "{label}";
    label.fill = am4core.color("#fff");
    label.verticalCenter = "middle";
    label.horizontalCenter = "middle";
    label.nonScaling =
      typeof igmRoundMarkersNonScaling !== "undefined"
        ? igmRoundMarkersNonScaling
        : true;
    label.fontSize =
      typeof igmClusterMarkerFontSize !== "undefined"
        ? igmClusterMarkerFontSize
        : data.markerDefaults.radius;
    label.clickable = false;
    label.focusable = false;
    label.hoverable = false;

    // check for custom tooltip template
    if (
      typeof data.roundMarkersTooltipTemplate !== "undefined" &&
      data.roundMarkersTooltipTemplate.trim() !== ""
    ) {
      markerSeriesTemplate.tooltipText = data.roundMarkersTooltipTemplate;
      markerSeriesTemplate.tooltipHTML = data.roundMarkersTooltipTemplate;
    } else {
      markerSeriesTemplate.tooltipText =
        data.tooltip && data.tooltip.template
          ? data.tooltip.template
          : "{tooltipContent}";
      markerSeriesTemplate.tooltipHTML =
        data.tooltip && data.tooltip.template
          ? data.tooltip.template
          : "{tooltipContent}";
    }

    markerSeriesTemplate.propertyFields.tooltipText = "tooltipTemplate";
    markerSeriesTemplate.propertyFields.tooltipHTML = "tooltipTemplate"; // fill can only be set if heatmap is not enabled and not a range

    if (data.heatMapMarkers && im.bool(data.heatMapMarkers.enabled)) {
      im.setupHeatMap(markerSeries, id, data); // setup the fill and radius if type is range

      if (
        typeof data.heatMapMarkers.type !== "undefined" &&
        data.heatMapMarkers.type === "range"
      ) {
        circle.propertyFields.radius = "radius";
        circle.propertyFields.fill = "fill";
      }
    } else {
      circle.propertyFields.radius = "radius";
      circle.propertyFields.fill = "fill";
    }

    circle.propertyFields.userClassName = "customClass"; // Set property fields

    markerSeriesTemplate.propertyFields.radius = "radius";
    markerSeriesTemplate.propertyFields.latitude = "latitude";
    markerSeriesTemplate.propertyFields.longitude = "longitude";
    markerSeriesTemplate.setStateOnChildren = true; // hover & active

    hoverState = circle.states.create("hover");
    hoverState.properties.fill = data.hover;
    hoverState.propertyFields.fill = "hover"; // active

    activeState = circle.states.create("active");
    activeState.properties.fill = data.hover;
    activeState.propertyFields.fill = "hover"; // highlight - for legend hover

    highlightState = circle.states.create("highlight");
    highlightState.properties.fill = data.hover;
    highlightState.propertyFields.fill = "hover"; // text label below

    if (data.roundMarkerLabels && im.bool(data.roundMarkerLabels.enabled)) {
      // create dummy state to force all object to get this state
      markerSeriesTemplate.states.create("hover");

      var markerLabel = markerSeriesTemplate.createChild(am4core.Label);
      var markerLabelPosition =
        typeof data.roundMarkerLabels.position !== "undefined" &&
        data.roundMarkerLabels.position !== ""
          ? data.roundMarkerLabels.position
          : "bottom";
      markerLabel.text =
        typeof data.roundMarkerLabels.source !== "undefined" &&
        data.roundMarkerLabels.source !== ""
          ? data.roundMarkerLabels.source
          : "{name}";
      markerLabel.fontSize = data.roundMarkerLabels.fontSize;

      // set state on parent
      markerLabel.setStateOnChildren = true;

      //for mobile devices
      if (
        data.roundMarkerLabels.mobileSize &&
        parseInt(data.roundMarkerLabels.mobileSize) !== 100
      ) {
        if (window.innerWidth <= 780) {
          markerLabel.fontSize =
            (parseInt(data.roundMarkerLabels.fontSize) *
              parseInt(data.roundMarkerLabels.mobileSize)) /
            100;
        }
      }

      markerLabel.nonScaling =
        typeof igmRoundMarkersNonScaling !== "undefined"
          ? igmRoundMarkersNonScaling
          : true;
      //im.bool(data.roundMarkerLabels.nonScaling);

      markerLabel.fill = data.roundMarkerLabels.fill;
      markerLabel.clickable = false;
      markerLabel.focusable = false;
      markerLabel.hoverable = false;

      // control position
      markerLabel.padding(0, 0, 0, 0);

      // this is a first iteration of this feature
      // in the future we can have the position individually for each marker and use the propertyFields approach
      if (markerLabelPosition === "bottom") {
        markerLabel.horizontalCenter = "middle";
        markerLabel.verticalCenter = "top";
        markerLabel.propertyFields.paddingTop = "radius";
      }

      if (markerLabelPosition === "top") {
        markerLabel.horizontalCenter = "middle";
        markerLabel.verticalCenter = "bottom";
        markerLabel.propertyFields.paddingBottom = "radius";
      }

      if (markerLabelPosition === "right") {
        markerLabel.horizontalCenter = "left";
        markerLabel.verticalCenter = "middle";
        markerLabel.propertyFields.paddingLeft = "radius";
      }

      if (markerLabelPosition === "left") {
        markerLabel.horizontalCenter = "right";
        markerLabel.verticalCenter = "middle";
        markerLabel.propertyFields.paddingRight = "radius";
      }
    }

    // send to front (so on overlay they are always on top of regions)
    markerSeries.zIndex = Number.MAX_VALUE;
    markerSeries.toFront();

    // Add data
    markerSeries.data = data.roundMarkers; // For legend color

    markerSeries.fill = data.markerDefaults.fill; // Events

    markerSeriesTemplate.events.on("hit", function (ev) {
      im.singleHit(id, ev);
      im.setupHitEvents(id, ev);
    });
    markerSeriesTemplate.events.on("over", function (ev) {
      im.setupHoverEvents(id, ev);
    });
  } // enable small map

  if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) {
    map.smallMap.series.push(markerSeries);
  } // add this series to map series to reference it later if needed

  im.maps[id].series.push(markerSeries); // if part of the parent map

  if (id === data.id) {
    // only add as base if not a cluster
    if (data.clusterMarkers && !im.bool(data.clusterMarkers.enabled)) {
      im.maps[id].allBaseSeries.push(markerSeries);
    } else if (data.clusterMarkers && im.bool(data.clusterMarkers.enabled)) {
      // it's a cluster, so the main series with all markers
      // markerSeries.hidden = true;
    }
  }

  return markerSeries;
};

iMapsManager.pushImageMarkerSeries = function (id, data) {
  var im = this,
    map = im.maps[id].map,
    // shorter reference for the map
    markerSeries,
    markerSeriesTemplate,
    imageMarker;

  if (Array.isArray(data.imageMarkers) && data.imageMarkers.length) {
    // Create image series
    markerSeries = map.series.push(new am4maps.MapImageSeries());
    markerSeries.name =
      data.imageMarkersLegend && data.imageMarkersLegend.title !== ""
        ? data.imageMarkersLegend.title
        : data.title;
    markerSeries.hiddenInLegend = data.imageMarkersLegend
      ? !im.bool(data.imageMarkersLegend.enabled)
      : false; // Create a circle image in image series template so it gets replicated to all new images

    markerSeriesTemplate = markerSeries.mapImages.template;

    //iOS focus scroll bug fix
    markerSeriesTemplate.focusable = false;

    imageMarker = markerSeriesTemplate.createChild(am4core.Image);
    imageMarker.propertyFields.href = "href";
    imageMarker.propertyFields.width = "size";
    imageMarker.propertyFields.height = "size";
    imageMarker.propertyFields.userClassName = "customClass"; //imageMarker.propertyFields.height = "height";

    imageMarker.propertyFields.horizontalCenter = "horizontalCenter";
    imageMarker.propertyFields.verticalCenter = "verticalCenter";
    imageMarker.nonScaling = true;
    imageMarker.propertyFields.nonScaling = "nonScaling";
    im.setupTooltip(id, markerSeries, data, imageMarker); // to have tooltip display above the image
    //imageMarker.tooltipX = am4core.percent(50);
    //imageMarker.tooltipY = am4core.percent(0);
    // check for custom tooltip template

    if (
      typeof data.imageMarkersTooltipTemplate !== "undefined" &&
      data.imageMarkersTooltipTemplate.trim() !== ""
    ) {
      imageMarker.tooltipText = data.imageMarkersTooltipTemplate;
      imageMarker.tooltipHTML = data.imageMarkersTooltipTemplate;
    } else {
      imageMarker.tooltipText = data.tooltip.template
        ? data.tooltip.template
        : "{tooltipContent}";
      imageMarker.tooltipHTML = data.tooltip.template
        ? data.tooltip.template
        : "{tooltipContent}";
    } // Set property fields

    markerSeriesTemplate.propertyFields.latitude = "latitude";
    markerSeriesTemplate.propertyFields.longitude = "longitude"; // Add data for the three cities

    // send to front (so on overlay they are always on top of regions)
    markerSeries.zIndex = Number.MAX_VALUE;
    markerSeries.toFront();

    markerSeries.data = data.imageMarkers; // Events

    markerSeriesTemplate.events.on("hit", function (ev) {
      im.singleHit(id, ev);
      im.setupHitEvents(id, ev);
    });
    markerSeriesTemplate.events.on("over", function (ev) {
      im.setupHoverEvents(id, ev);
    });
  } // enable small map

  if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) {
    map.smallMap.series.push(markerSeries);
  } // add this series to map series to reference it later if needed

  im.maps[id].series.push(markerSeries);
  return markerSeries;
};

iMapsManager.pushIconMarkerSeries = function (id, data) {
  var im = this,
    map = im.maps[id].map,
    // shorter reference for the map
    markerSeries,
    markerSeriesTemplate,
    icon,
    hover,
    active,
    highlightState,
    label,
    clickableOverlay;

  if (Array.isArray(data.iconMarkers) && data.iconMarkers.length) {
    // Create image series
    markerSeries = map.series.push(new am4maps.MapImageSeries());
    markerSeries.hiddenInLegend = data.iconMarkersLegend
      ? !im.bool(data.iconMarkersLegend.enabled)
      : false;
    markerSeries.name =
      data.iconMarkersLegend && data.iconMarkersLegend.title !== ""
        ? data.iconMarkersLegend.title
        : data.title;
    markerSeriesTemplate = markerSeries.mapImages.template;
    markerSeriesTemplate.nonScaling = true;
    markerSeriesTemplate.setStateOnChildren = true; //apply parent's current state to children

    //iOS focus scroll bug fix
    markerSeriesTemplate.focusable = false;

    markerSeriesTemplate.states.create("hover"); //create dummy state for hover
    // Create a circle image in image series template so it gets replicated to all new images

    icon = markerSeriesTemplate.createChild(am4core.Sprite);
    icon.propertyFields.scale = "scale";
    icon.propertyFields.path = "path";
    icon.propertyFields.rotation = "rotation";

    /*
		if (typeof data.iconMarkersTooltipTemplate !== 'undefined' && data.iconMarkersTooltipTemplate.trim() !== '') {
			icon.tooltipText = data.iconMarkersTooltipTemplate;
			icon.tooltipHTML = data.iconMarkersTooltipTemplate;
		} else {
			icon.tooltipText = data.tooltip.template ? data.tooltip.template : "{tooltipContent}";
			icon.tooltipHTML = data.tooltip.template ? data.tooltip.template : "{tooltipContent}";
		}*/

    icon.propertyFields.horizontalCenter = "horizontalCenter";
    icon.propertyFields.verticalCenter = "verticalCenter";
    icon.propertyFields.fill = "fill"; // For legend color

    markerSeries.fill = data.iconMarkerDefaults.fill; // clickable overlay

    clickableOverlay = markerSeriesTemplate.createChild(am4core.Sprite);
    clickableOverlay.propertyFields.scale = "scale";
    clickableOverlay.path = "M-10,0a10,10 0 1,0 20,0a10,10 0 1,0 -20,0";
    clickableOverlay.opacity = 0;
    clickableOverlay.propertyFields.horizontalCenter = "horizontalCenter";
    clickableOverlay.propertyFields.verticalCenter = "verticalCenter";

    markerSeriesTemplate.tooltipText = data.tooltip.template
      ? data.tooltip.template
      : "{tooltipContent}";
    markerSeriesTemplate.tooltipHTML = data.tooltip.template
      ? data.tooltip.template
      : "{tooltipContent}";

    im.setupTooltip(id, markerSeries, data, clickableOverlay);

    if (data.iconMarkerLabels && im.bool(data.iconMarkerLabels.enabled)) {
      var markerLabel = markerSeriesTemplate.createChild(am4core.Label);
      markerLabel.text =
        typeof data.iconMarkerLabels.source !== "undefined" &&
        data.iconMarkerLabels.source !== ""
          ? data.iconMarkerLabels.source
          : "{name}";
      markerLabel.horizontalCenter = "middle";
      markerLabel.verticalCenter = "top";
      markerLabel.fontSize = data.iconMarkerLabels.fontSize;
      markerLabel.nonScaling = false;
      markerLabel.fill = data.iconMarkerLabels.fill;
      markerLabel.clickable = false;
      markerLabel.focusable = false;
      markerLabel.hoverable = false;
      markerLabel.padding(0, 0, 0, 0);
      markerLabel.adapter.add("dy", function (dy, target) {
        var icon = target.parent.children.getIndex(0);
        var scale = icon.scale;
        var space;

        if (icon.verticalCenter === "middle") {
          space = 10 * scale;
        }

        if (icon.verticalCenter === "bottom") {
          space = 0;
        }

        if (icon.verticalCenter === "top") {
          space = 20 * scale + 5;
        } // formula to get the label to distance at an accepted level

        return space;
      });
    } // hover & active

    hover = icon.states.create("hover");
    hover.propertyFields.fill = "hover";
    active = icon.states.create("active");
    active.propertyFields.fill = "hover"; // highlight - for legend hover

    highlightState = icon.states.create("highlight");
    highlightState.properties.fill = data.hover;
    highlightState.propertyFields.fill = "hover"; // Set property fields

    markerSeriesTemplate.propertyFields.latitude = "latitude";
    markerSeriesTemplate.propertyFields.longitude = "longitude"; // Add data

    // send to front (so on overlay they are always on top of regions)
    markerSeries.zIndex = Number.MAX_VALUE;
    markerSeries.toFront();

    markerSeries.data = data.iconMarkers; // Events

    markerSeriesTemplate.events.on("hit", function (ev) {
      im.singleHit(id, ev);
      im.setupHitEvents(id, ev);
    });
    markerSeriesTemplate.events.on("over", function (ev) {
      im.setupHoverEvents(id, ev);
    });
  } // enable small map

  if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) {
    map.smallMap.series.push(markerSeries);
  } // add this series to map series to reference it later if needed

  im.maps[id].series.push(markerSeries);
  return markerSeries;
};

iMapsManager.pushLineSeries = function (id, data) {
  var im = this,
    map = im.maps[id].map,
    // shorter reference for the map
    lineSeries = {},
    lineData = []; // Lines

  if (Array.isArray(data.lines) && data.lines.length) {
    // Add line series
    if (data.projection === "Orthographic") {
      lineSeries = map.series.push(new am4maps.MapLineSeries());
      lineSeries.mapLines.template.propertyFields.shortestDistance = true;
    } else {
      lineSeries = map.series.push(new am4maps.MapArcSeries());
      lineSeries.mapLines.template.line.propertyFields.controlPointDistance =
        "curvature";
      lineSeries.mapLines.template.line.controlPointPosition = 0.5;
    }

    lineSeries.name =
      data.linesLegend && data.linesLegend.title !== ""
        ? data.linesLegend.title
        : data.title;
    lineSeries.hiddenInLegend = data.linesLegend
      ? !im.bool(data.linesLegend.enabled)
      : false;
    lineSeries.mapLines.template.nonScalingStroke = true;
    lineSeries.mapLines.template.propertyFields.strokeWidth = "strokeWidth";
    lineSeries.mapLines.template.propertyFields.strokeDasharray = "strokeDash";
    lineSeries.mapLines.template.propertyFields.stroke = "stroke";

    // arrow
    lineSeries.mapLines.template.arrow.position = 1; // 1 is the end
    lineSeries.mapLines.template.arrow.nonScaling = true;
    lineSeries.mapLines.template.arrow.propertyFields.fill = "stroke";
    lineSeries.mapLines.template.arrow.horizontalCenter = "middle";
    //lineSeries.mapLines.template.arrow.tooltipHTML = "{title}";

    //lineSeries.mapLines.template.arrow.disabled = true;
    lineSeries.mapLines.template.arrow.propertyFields.disabled =
      "arrowDisabled";

    data.lines.forEach(function (lineObj, index) {
      // make sure multiGeoLine is array of arrays:
      lineObj.multiGeoLine = [lineObj.multiGeoLine];
      lineData.push(lineObj);
    });

    // events - to do
    lineSeries.mapLines.template.events.on("hit", function (ev) {
      im.singleHit(id, ev);
      im.setupHitEvents(id, ev);
    });
    lineSeries.mapLines.template.events.on("over", function (ev) {
      im.setupHoverEvents(id, ev);
    });

    lineSeries.data = lineData;

    // bring to front
    lineSeries.zIndex = Number.MAX_VALUE - 1;
    lineSeries.toFront();

    // enable small map
    if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) {
      map.smallMap.series.push(lineSeries);
    } //let's hide this from legend, since they don't group in the same Series
    //lineSeries.hiddenInLegend = true;
    // add this series to map series to reference it later if needed

    im.maps[id].series.push(lineSeries); // For legend color

    lineSeries.fill = data.lineDefaults.stroke; // if it's part of the parent map

    if (id === data.id) {
      im.maps[id].allBaseSeries.push(lineSeries);
    }
  }

  return lineSeries;
};

iMapsManager.pushLabelSeries = function (id, data) {
  var im = this,
    map = im.maps[id].map,
    // shorter reference for the map
    labelSeries,
    labelSeriesTemplate,
    label,
    activeState,
    highlightState,
    hoverState,
    background = false;

  if (Array.isArray(data.labels) && data.labels.length) {
    // Create image series
    labelSeries = map.series.push(new am4maps.MapImageSeries());
    labelSeries.name =
      data.labelsLegend && data.labelsLegend.title !== ""
        ? data.labelsLegend.title
        : data.title;
    labelSeries.hiddenInLegend = data.labelsLegend
      ? !im.bool(data.labelsLegend.enabled)
      : false;

    labelSeriesTemplate = labelSeries.mapImages.template;
    labelSeriesTemplate.setStateOnChildren = true;

    // label
    label = labelSeriesTemplate.createChild(am4core.Label);
    label.text = "{id}";

    //label.nonScaling = true;
    label.nonScaling =
      typeof igmLabelsNonScaling !== "undefined" ? igmLabelsNonScaling : true;

    if (data.labelStyle) {
      label.fontFamily = data.labelStyle.fontFamily;
      label.fontWeight = data.labelStyle.fontWeight;
    }

    label.horizontalCenter = data.labelPosition.horizontalCenter;
    label.verticalCenter = data.labelPosition.verticalCenter;
    label.propertyFields.fill = "fill";
    label.propertyFields.fontSize = "fontSize";
    im.setupTooltip(id, labelSeries, data, label); // custom fields

    if (
      typeof data.labelsTooltipTemplate !== "undefined" &&
      data.labelsTooltipTemplate.trim() !== ""
    ) {
      label.tooltipText = data.labelsTooltipTemplate;
      label.tooltipHTML = data.labelsTooltipTemplate;
    } else {
      label.tooltipText =
        data.tooltip && data.tooltip.template
          ? data.tooltip.template
          : "{tooltipContent}";
      label.tooltipHTML =
        data.tooltip && data.tooltip.template
          ? data.tooltip.template
          : "{tooltipContent}";
    }

    // Set property fields
    labelSeriesTemplate.propertyFields.latitude = "latitude";
    labelSeriesTemplate.propertyFields.longitude = "longitude";
    labelSeriesTemplate.propertyFields.fill = "fill";
    labelSeriesTemplate.propertyFields.fontSize = "fontSize";
    label.propertyFields.verticalCenter = "verticalCenter";
    label.propertyFields.horizontalCenter = "horizontalCenter";

    // labels background - currently only possible through global
    background =
      typeof igmLabelsBackground !== "undefined" ? igmLabelsBackground : false;
    if (typeof background === "object") {
      label.background = new am4core.RoundedRectangle();
      label.background.cornerRadius(...background.cornerRadius);
      label.background.fill = am4core.color(background.color);
      label.padding(...background.padding);
      label.background.stroke = am4core.color(background.stroke);
    }

    // hover & active
    hoverState = label.states.create("hover");
    hoverState.properties.fill = data.hover;
    hoverState.propertyFields.fill = "hover"; // active

    activeState = label.states.create("active");
    activeState.properties.fill = data.hover;
    activeState.propertyFields.fill = "hover"; // highlight - for legend hover

    highlightState = label.states.create("highlight");
    highlightState.properties.fill = data.hover;
    highlightState.propertyFields.fill = "hover";

    // send to front (so on overlay they are always on top of regions)
    labelSeries.zIndex = Number.MAX_VALUE;
    labelSeries.toFront();

    // Add data
    labelSeries.data = data.labels; // For legend color

    labelSeries.fill = data.labelDefaults.fill; // Events

    labelSeriesTemplate.events.on("hit", function (ev) {
      im.singleHit(id, ev);
      im.setupHitEvents(id, ev);
    });
    labelSeriesTemplate.events.on("over", function (ev) {
      im.setupHoverEvents(id, ev);
    });
  } // enable small map

  if (data.zoom && data.zoom.smallMap && im.bool(data.zoom.smallMap)) {
    map.smallMap.series.push(labelSeries);
  } // add this series to map series to reference it later if needed

  im.maps[id].labelSeries.push(labelSeries);
  im.maps[id].series.push(labelSeries); // if part of the parent map

  if (id === data.id) {
    im.maps[id].allBaseSeries.push(labelSeries);
  }

  return labelSeries;
};

iMapsManager.setupTooltip = function (id, series, data, marker) {
  var im = this,
    tooltip = data.tooltip,
    map = im.maps[id].map,
    shadow;
  marker = marker || false; // don't include it in export
  // regionSeries.tooltip.exportable = false;
  // default behaviour

  if (typeof data.tooltip === "undefined") {
    series.tooltip.disabled = false;
    series.tooltip.getFillFromObject = false;
    series.tooltip.getStrokeFromObject = false;
    series.tooltip.label.fill = am4core.color("#000000");
    series.tooltip.background.fill = am4core.color("#FFFFFF");
    return;
  }

  if (!im.bool(data.tooltip.enabled)) {
    series.tooltip.disabled = true;
    return series;
  }

  // if it's disabled on mobile/smaller screens
  if (
    _typeof(data.tooltip.disableMobile) !== undefined &&
    im.bool(data.tooltip.disableMobile) &&
    window.innerWidth <= 780
  ) {
    series.tooltip.disabled = true;
    return series;
  }

  //if it's overlay, it might have a custom config

  // tooltip settings from map config
  series.tooltip.label.interactionsEnabled = im.bool(tooltip.fixed);
  series.tooltip.background.cornerRadius = tooltip.cornerRadius;
  if (tooltip.pointerLength) {
    series.tooltip.background.pointerLength = parseInt(tooltip.pointerLength);
  }
  series.tooltip.getFillFromObject = false;
  series.tooltip.getStrokeFromObject = false;
  series.tooltip.label.fill = tooltip.color;
  series.tooltip.background.fill = tooltip.backgroundColor;
  series.tooltip.fontFamily = tooltip.fontFamily;
  series.tooltip.fontSize = tooltip.fontSize;
  series.tooltip.fontWeight = tooltip.fontWeight;

  // animation - disable the fly out effect on markers
  series.tooltip.animationDuration = 0;

  // border
  if (tooltip.borderColor !== "undefined") {
    series.tooltip.background.stroke = tooltip.borderColor;
    series.tooltip.background.strokeWidth = tooltip.borderWidth;
  }

  if (typeof tooltip.maxWidth !== "undefined" && tooltip.maxWidth !== "") {
    series.tooltip.maxWidth = parseInt(tooltip.maxWidth);
    series.tooltip.contentWidth = parseInt(tooltip.maxWidth);
  }
  series.tooltip.label.wrap = true;

  // box-shadow
  if (
    typeof tooltip.customShadow !== "undefined" &&
    im.bool(tooltip.customShadow)
  ) {
    shadow = series.tooltip.background.filters.getIndex(0);
    shadow.dx = tooltip.customShadowOpts.dx;
    shadow.dy = tooltip.customShadowOpts.dy;
    shadow.blur = tooltip.customShadowOpts.blur;
    shadow.opacity = tooltip.customShadowOpts.opacity;
    shadow.color = tooltip.customShadowOpts.color;
  }

  // Set up fixed tooltips
  if (im.bool(tooltip.fixed)) {
    if (tooltip.showTooltipOn === "always") {
      //allow tooltip to be hidden outside viewport
      series.tooltip.ignoreBounds = true;

      series.tooltip.pointerOrientation = (function (toolTipPosition) {
        switch (toolTipPosition) {
          case "horizontal":
            return "horizontal";
          case "below":
            return "up";
          case "above":
            return "down";
          case "right":
            return "left";
          case "left":
            return "right";
          default:
            return "vertical";
        }
      })(tooltip.toolTipPosition);
    }

    if (series.mapPolygons) {
      series.calculateVisualCenter = true;
      series.mapPolygons.template.tooltipPosition = "fixed";

      // in case it's a group and the tooltip is set to fixed,
      // we need to set the tooltip hover to have the regions keep the hover state
      if (series.groupHover) {
        // set to false so that the tooltip does not
        // inherit the highlight state color on hover
        series.tooltip.getFillFromObject = false;
        series.tooltip.events.on("over", function (ev) {
          ev.target.dataItem.component.mapPolygons.each(function (polygon) {
            polygon.setState("highlight");
          });
        });
        series.tooltip.events.on("out", function (ev) {
          ev.target.dataItem.component.mapPolygons.each(function (polygon) {
            polygon.setState("default");
          });
        });
      }

      series.tooltip.keepTargetHover = true;

      if (tooltip.showTooltipOn) {
        series.mapPolygons.template.showTooltipOn = tooltip.showTooltipOn;

        // to have the tooltip in the marker fixed, even after over out
        if (tooltip.showTooltipOn === "hit") {
          series.tooltip.keepTargetHover = false;
        }
      }
    } else {
      // for markers
      series.mapImages.template.tooltipPosition = "fixed";
      series.tooltip.keepTargetHover = true;

      if (tooltip.showTooltipOn && marker) {
        series.mapImages.template.showTooltipOn = tooltip.showTooltipOn;
        marker.showTooltipOn = tooltip.showTooltipOn;

        // to have the tooltip in the marker fixed, even after over out
        if (tooltip.showTooltipOn === "hit") {
          series.tooltip.keepTargetHover = false;
        }

        // to have the tooltip display after the map loads
        if (tooltip.showTooltipOn === "always") {
          map.events.on("appeared", function () {
            marker.clones.each(function (clone) {
              clone.showTooltip();
            });
          });

          map.events.on("mappositionchanged", function (ev) {
            marker.clones.each(function (clone) {
              clone.showTooltip();
            });
          });
        }
      }
    }
  }
  return series;
};

iMapsManager.prepareURL = function (str) {
  if (typeof str !== "string") {
    return str;
  }

  // parse html entities
  str = str.replace(/&amp;/gi, "&");
  str.replace(/&#(\d+);/g, function (match, dec) {
    return String.fromCharCode(dec);
  });

  // check if allowed url
  var url, protocols;
  try {
    url = new URL(str);
  } catch (_) {
    url = false;
  }

  // check if valid url. If string it might be a anchor or relative path, so keep going
  if (url) {
    // acepted protocols
    protocols = [null, "http:", "https:", "mailto:", "tel:"];
    if (!protocols.includes(url.protocol)) {
      console.log("URL protocol not allowed");
      return "";
    }
  }

  return str;
};

iMapsManager.setupHitEvents = function (id, ev) {
  var im = this,
    data = im.maps[id].data,
    dataContext,
    map = im.maps[id].map,
    customActionName,
    targetType = im.getTargetSeriesType(ev.target),
    clicked = im.maps[id].clicked || false,
    zoomCluster = data.clusterMarkers
      ? parseFloat(data.clusterMarkers.zoomLevel)
      : 1,
    currentRegion,
    mapFunction,
    container = document.getElementById(data.container),
    event = new Event("mapEntryClicked");

  if (ev.target.isLabels) {
    dataContext = ev.target.dataItems.first.dataContext;
  } else {
    dataContext = ev.target.dataItem.dataContext;
  }

  // on certain devices, the click gets triggered twice, so the 'hold click actions' won't work properly
  // 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
  if (map.lastClickedEntry === ev.target && !im.maps[id].clicked) {
    // the clicked is used to control the "hold click actions" feature
    im.maps[id].clicked = dataContext;

    // we reset after 1 second, to allow real second clicks to run
    // this also has the side effect that when the 'hold click actions' is enabled, only after 1 second, the second tap will actually work
    setTimeout(function () {
      map.lastClickedEntry = null;
      im.maps[id].clicked = null;
    }, 1000);

    return;
  }

  // we reset it to null, in case it was clicked before, double tap
  im.maps[id].clicked = null;
  map.lastClickedEntry = ev.target;
  container.dispatchEvent(event);

  // if it's a cluster, zoom in a bit
  if (targetType === "MapImage") {
    if (dataContext.cluster) {
      // if we're far from the max, let's just zoom half
      if (zoomCluster - parseInt(map.zoomLevel) > 5) {
        zoomCluster = parseInt(map.zoomLevel) + zoomCluster / 2;
      }
      ev.target.series.chart.zoomToMapObject(ev.target, zoomCluster);
    }
  } // for admin log

  console.log(dataContext); // Zoom on click

  if (
    data.zoom &&
    im.bool(data.zoom.enabled) &&
    im.bool(data.zoom.zoomOnClick)
  ) {
    ev.zooming = true;
    im.zoomToRegion(ev, id);
  } // drill down

  if (targetType === "MapPolygon" && im.bool(data.drillDownOnClick)) {
    im.drillDown(id, ev);
  }

  if (dataContext.madeFromGeoData) {
    // check if we should clear selected or not?
    // iMapsManager.clearSelected(id);

    mapFunction = "igm_inactive_" + data.id;

    if (typeof window[mapFunction] === "function") {
      data = window[mapFunction](data);
    }

    return;
  }

  // if it's a marker with action to open specific map
  if (
    targetType === "MapImage" &&
    _typeof(dataContext.action) !== undefined &&
    dataContext.action === "igm_display_map"
  ) {
    if (
      Array.isArray(
        iMapsManager.maps[id].seriesById[parseInt(dataContext.content)]
      )
    ) {
      currentRegion =
        iMapsManager.maps[id].seriesById[parseInt(dataContext.content)];
      im.drillTo(id, ev, currentRegion, true);
    }
  } // if admin, we don't trigger the actions

  if (im.bool(data.admin)) {
    return;
  } // if we need to hold action on mobile/smaller screens to show tooltip

  if (
    data.tooltip &&
    _typeof(data.tooltip.enabled) !== undefined &&
    im.bool(data.tooltip.enabled) &&
    _typeof(data.tooltip.disableMobile) !== undefined &&
    !im.bool(data.tooltip.disableMobile) &&
    _typeof(data.tooltip.holdAction) !== undefined &&
    im.bool(data.tooltip.holdAction) &&
    window.innerWidth <= 780
  ) {
    if (!clicked || clicked !== dataContext) {
      console.log("Holding action for second tap.");
      im.maps[id].clicked = dataContext;
      return;
    }

    // in case the previously clicked will now perform action, we reset it
    if (clicked === dataContext) {
      im.maps[id].clicked = false;
    } // if it's a different one, set it normally
    else {
      im.maps[id].clicked = dataContext;
    }
  }

  if (dataContext.action === "none") {
    // do nothing
    return;
  }

  // check urls
  if (
    dataContext.action === "open_url" ||
    dataContext.action === "open_url_new"
  ) {
    dataContext.content = iMapsManager.prepareURL(dataContext.content);
  }
  // open new url
  if (dataContext.action === "open_url" && dataContext.content !== "") {
    document.location = dataContext.content;
  } else if (
    dataContext.action === "open_url_new" &&
    dataContext.content !== ""
  ) {
    window.open(dataContext.content);
  } // custom
  else if (dataContext.action && dataContext.action.includes("custom")) {
    customActionName = dataContext.action + "_" + dataContext.mapID;

    if (typeof window[customActionName] !== "undefined") {
      window[customActionName](dataContext);
    }
  } else {
    if (typeof window[dataContext.action] !== "undefined") {
      window[dataContext.action](id, dataContext);
    }
  }
};

iMapsManager.zoomToMap = function (ev, target, id) {
  var im = this;
  var baseMap = false;

  target.forEach(function (series) {
    if ("MapPolygonSeries" == im.getTargetSeriesType(series)) {
      baseMap = series;
      return;
    }
  });

  if (baseMap) {
    ev.target.series.chart.zoomToRectangle(
      baseMap.north,
      baseMap.east,
      baseMap.south,
      baseMap.west,
      1,
      true
    );
    console.log("zoomed to specific map");
    return;
  }
  console.log("failed to zoom to specific map");
};

iMapsManager.zoomToRegion = function (ev, id) {
  var im = this,
    seriesType = im.getTargetSeriesType(ev.target),
    data = im.maps[id].data,
    map = im.maps[id].map,
    markerZoomLevel,
    dataContext; // do nothing if we clicked a label

  if (ev.target.isLabels) {
    return;
  }

  dataContext = ev.target.dataItem.dataContext; // if it's a marker, handle it differently

  if (seriesType == "MapImage") {
    // if it's a cluster marker
    if (dataContext.cluster) {
      //do nothing, we already zoomed
    } else {
      markerZoomLevel =
        typeof igmMarkerZoomLevelOnClick !== "undefined"
          ? igmMarkerZoomLevelOnClick
          : ev.target.parent.chart.zoomLevel * 2;
      ev.target.series.chart.zoomToMapObject(ev.target, markerZoomLevel, true);
    }
  } else {
    if (dataContext.id === "asia" && !data.map.startsWith("continents")) {
      ev.target.series.chart.deltaLongitudeOriginal =
        ev.target.series.chart.deltaLongitude;
      ev.target.series.chart.deltaLongitude = -10;
      ev.target.series.chart.zoomToGeoPoint(
        {
          latitude: 34.076842,
          longitude: 100.693068,
        },
        1.7,
        true
      );
    } else if (dataContext.id === "asia" && data.map.startsWith("continents")) {
      ev.target.series.chart.zoomToGeoPoint(
        {
          latitude: 34.076842,
          longitude: 100.693068,
        },
        2.2,
        true
      );
    } else if (
      dataContext.id === "northAmerica" &&
      data.map.startsWith("region/world/worldRegion")
    ) {
      ev.target.series.chart.zoomToGeoPoint(
        {
          latitude: 55.5,
          longitude: -105.5,
        },
        3,
        true
      );
    } else if (
      dataContext.id === "northAmerica" &&
      data.map.startsWith("continents")
    ) {
      ev.target.series.chart.zoomToGeoPoint(
        {
          latitude: 55.5,
          longitude: -105.5,
        },
        2.3,
        true
      );
    } else if (dataContext.id === "ZA" && data.map.startsWith("world")) {
      ev.target.series.chart.zoomToGeoPoint(
        {
          latitude: -28.6,
          longitude: 24.7,
        },
        12.2,
        true
      );
    } else if (dataContext.id === "US" && data.map.startsWith("world")) {
      ev.target.series.chart.zoomToGeoPoint(
        {
          latitude: 49,
          longitude: -119,
        },
        2.6,
        true
      );
    } else if (dataContext.id === "RU" && data.map.startsWith("world")) {
      ev.target.series.chart.zoomToGeoPoint(
        {
          latitude: 64.5,
          longitude: 105,
        },
        2.1,
        true
      );
    } else {
      if (
        typeof ev.target.series.chart.deltaLongitudeOriginal !== "undefined"
      ) {
        ev.target.series.chart.deltaLongitude =
          ev.target.series.chart.deltaLongitude;
      }

      ev.target.series.chart.zoomToMapObject(
        ev.target,
        ev.target.zoomLevel * 2
      );
    }
  }
};

iMapsManager.setupHoverEvents = function (id, ev) {
  var im = this,
    selected = im.maps[id].selected || false,
    dataContext;

  if (ev.target.isLabels) {
    dataContext = ev.target.dataItems.first.dataContext;
  } else {
    dataContext = ev.target.dataItem.dataContext;
  }

  // when tooltip position is fixed, hovering regions might leave hovered state behind, when the overout goes through the tooltip
  if (
    ev.target.className !== "MapImage" &&
    ev.target.tooltipPosition === "fixed"
  ) {
    if (Array.isArray(im.tempHover) && im.tempHover.length >= 0) {
      im.tempHover[0].setState("default");
      im.tempHover = [];
    }
    im.tempHover = [ev.target];
  }

  if (dataContext.action && dataContext.action !== "none") {
    ev.target.cursorOverStyle = am4core.MouseCursorStyle.pointer;
  }

  if (
    Array.isArray(selected) &&
    !selected.includes(ev.target) &&
    ev.target.dataItem &&
    typeof ev.target.dataItem.dataContext.madeFromGeoData === "undefined"
  ) {
    selected.forEach(function (sel, index) {
      if (typeof sel === "object" && typeof sel.isHover !== "undefined") {
        sel.isHover = false;
      }
    });
  }

  // we exclude touch devices, since the hover event is also triggered on tap, otherwise we have 2 select events triggered
  if (
    im.bool(dataContext.triggerClickOnHover) &&
    (!iMapsManager.isTouchScreendevice() || ev.type === "over") &&
    (typeof iMaps.maps[id].mapClicked === "undefined" ||
      iMaps.maps[id].mapClicked === false)
  ) {
    iMapsManager.select(
      id,
      dataContext.id,
      false,
      true,
      dataContext.mapID,
      false
    );
  }

  // if it's a marker and we want to trigger hover event also on associated regions in marker value
  if (
    im.bool(dataContext.triggerRegionHover) &&
    dataContext.val &&
    dataContext.val !== ""
  ) {
    iMapsManager.hover(id, dataContext.val);
    ev.target.events.on("out", function (ev) {
      iMapsManager.clearHovered(id);
    });
  }
};

iMapsManager.singleHit = function (id, ev) {
  var im = this,
    dataContext,
    data = im.maps[id].data;

  if (ev.target.isLabels) {
    dataContext = ev.target.dataItems.first.dataContext;
  } else {
    dataContext = ev.target.dataItem.dataContext;
  }

  if (dataContext.madeFromGeoData) {
    return;
  }

  if (iMaps.maps[id].activeStateControl === true && dataContext.activeState) {
    iMaps.maps[id].mapClicked = true;
  }

  // causes issues on tap on mobile, we need to click twice?
  // seems the clear selected is affecting

  // update:
  // let's try passing second argument as false if tooltip is set to display on click?
  let keepThis = true;
  if (data.tooltip && data.tooltip.showTooltipOn === "hit") {
    keepThis = false;
  }
  iMapsManager.clearSelected(id, keepThis, true);

  ev.target.isActive = true;
  ev.target.isHover = true;
  ev.target.setState("active");
  im.maps[id].selected = [ev.target];
};

iMapsManager.groupHit = function (id, ev) {
  var im = this,
    selected = im.maps[id].selected || false;

  if (ev.target.dataItem.dataContext.madeFromGeoData) {
    return;
  }

  if (
    iMaps.maps[id].activeStateControl === true &&
    ev.target.dataItem.dataContext.activeState
  ) {
    iMaps.maps[id].mapClicked = true;
  }

  //removed to improve click on group ?
  iMapsManager.clearSelected(id);

  selected = [];

  ev.target.parent.mapPolygons.each(function (polygon) {
    // added the typeof contorl to only run code on original clicked/hit entry and not the individual shape
    // removed, because the code wasn't runnning otherwise -> && typeof polygon.dataItem.dataContext.originalID === 'undefined'
    if (!polygon.dataItem.dataContext.madeFromGeoData) {
      // toggle active state
      polygon.setState("active");

      if (ev.target === polygon) {
        polygon.isHover = true;
      }

      //removed to fix bug when hovering groups after clicked ?
      polygon.isActive = true;
      polygon.isGroupActive = true;
      selected.push(polygon);
    }
  });

  im.maps[id].selected = selected;
};

iMapsManager.groupHover = function (id, ev) {
  var im = this,
    dataContext;

  if (ev.target.isLabels) {
    dataContext = ev.target.dataItems.first.dataContext;
  } else {
    dataContext = ev.target.dataItem.dataContext;
  }

  if (ev.target.dataItem.dataContext.madeFromGeoData) {
    return;
  }

  if (
    im.bool(dataContext.triggerClickOnHover) &&
    (typeof iMaps.maps[id].mapClicked === "undefined" ||
      iMaps.maps[id].mapClicked === false)
  ) {
    iMapsManager.select(
      id,
      dataContext.id,
      false,
      true,
      dataContext.mapID,
      false
    );
  } // set mouse hover pointer cursor

  if (
    ev.target.dataItem.dataContext.action &&
    ev.target.dataItem.dataContext.action != "none"
  ) {
    ev.target.cursorOverStyle = am4core.MouseCursorStyle.pointer;
  } // hilight all polygons from this group

  ev.target.parent.mapPolygons.each(function (polygon) {
    if (!polygon.dataItem.dataContext.madeFromGeoData) {
      if (!polygon.isActive) {
        polygon.setState("highlight");
      }
    }
  });
};

iMapsManager.groupHoverOut = function (id, ev) {
  if (ev.target.dataItem.dataContext.madeFromGeoData) {
    return;
  }

  ev.target.parent.mapPolygons.each(function (polygon) {
    if (!polygon.isGroupActive) {
      polygon.setState("default");
      polygon.isActive = false;
      polygon.isHover = false;
    }
  });
};

/**
 * Get regions object collection based on value of a specific parameter
 * id - map id
 * value - value you want to filter by
 * parameter - parameter to search
 */
iMapsManager.getRegionsByValue = function (id, value, parameter) {
  var im = this,
    map = im.maps[id],
    series = map.series,
    seriesType = false,
    regions = [],
    tempArray;

  value = value || "*";
  parameter = parameter || "val";

  series.forEach(function (serie) {
    seriesType = im.getTargetSeriesType(serie);
    if (seriesType === "MapPolygonSeries") {
      tempArray = serie.mapPolygons.values.filter(function (entry) {
        if (entry.dataItem.dataContext[parameter] === value || value === "*") {
          return true;
        }
      });
      regions = regions.concat(tempArray);
    }
  });

  return regions;
};

/** Get markers object collection based on value of a specific parameter
 * It will search for all kinds of markers (icons, images, round markers)
 * id - map id
 * value - value you want to filter by
 * parameter - parameter to search
 */
iMapsManager.getMarkersByValue = function (id, value, parameter) {
  var im = this,
    map = im.maps[id],
    series = map.series,
    seriesType = false,
    markers = [],
    tempArray;

  value = value || "*";
  parameter = parameter || "val";

  series.forEach(function (serie) {
    seriesType = im.getTargetSeriesType(serie);
    if (seriesType === "MapImageSeries") {
      tempArray = serie.mapImages.values.filter(function (entry) {
        if (entry.dataItem.dataContext[parameter] === value || value === "*") {
          return true;
        }
      });
      markers = markers.concat(tempArray);
    }
  });

  return markers;
};

/**
 * Selects a element in the map
 * id - id of the map
 * elID - id of the element to select
 * forceFixedTooltip - if we should force the tooltip to be fixed or not
 * showTooltip
 * seriesMapID - map id of the series, which could be ab overlay
 * click - boolean - trigger true click event
 */

iMapsManager.select = function (
  id,
  elID,
  forceFixedTooltip,
  showTooltip,
  seriesMapID,
  click
) {
  var im = this,
    map = im.maps[id],
    data = im.maps[id].data,
    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
    selected = [],
    select,
    group,
    defaultTooltipPosition,
    defaultTooltipShowOn,
    seriesByID = iMaps.maps[id].seriesById,
    thisSeries = false,
    ogID,
    customRegionGroup = true,
    triggered = false, // temp solution to prevent map from triggering multiple click actions if there are entries with same ID in different layers
    isGroup = false,
    keepThis = true;

  // map sure it's string
  if (Number.isInteger(elID)) {
    elID = elID.toString();
  }

  ogID = elID;

  // Force fixed position?
  if (typeof forceFixedTooltip === "undefined") {
    forceFixedTooltip = true;
  }

  if (typeof showTooltip === "undefined") {
    showTooltip = true;
  }

  if (typeof seriesMapID === "undefined") {
    seriesMapID = id;
  }

  if (seriesByID.hasOwnProperty(seriesMapID)) {
    thisSeries = seriesByID[seriesMapID];
  }

  if (typeof click === "undefined") {
    click = true;
  } else {
    click = false;
  }

  // we pass the click as the 'skipReset' - if not a real click, its the hover and we skip the reset to prevent flickering
  // second argument, the keepThis set to true, to avoid removing hover status of selected/hovered entry
  // update - second argument might be set to false, if tooltip only displays on hit to fix issue where the tooltip wouldn't close
  if (data.tooltip && data.tooltip.showTooltipOn === "hit") {
    keepThis = false;
  }

  iMapsManager.clearSelected(id, keepThis, !click);

  if (click) {
    iMaps.maps[id].activeStateControl = true;
  } else {
    iMaps.maps[id].activeStateControl = false;
  }

  // if we have to search in specific series and this is not that one, skip
  if (thisSeries) {
    series = thisSeries;
  }

  if (Array.isArray(series)) {
    for (var i = 0, len = series.length; i < len; i++) {
      if (triggered) {
        break;
      }

      // if it's one of the cluster series, ignore
      if (typeof series[i].isCluster !== "undefined" && series[i].isCluster) {
        continue;
      }
      // regionSeries
      if (series[i].mapPolygons) {
        //first trigger click
        // this might return false if there is no entry created for this group and we are selecting programatically
        select = series[i].getPolygonById(elID);

        if (
          select &&
          typeof select.dataItem.dataContext.originalID !== "undefined" &&
          select.dataItem.dataContext.originalID.includes(",")
        ) {
          elID = select.dataItem.dataContext.originalID;
        }

        // show series if it's hidden
        if (select) {
          series[i].show();
        }

        // check if group
        if (elID.includes(",")) {
          if (typeof select !== "undefined" && select) {
            select.tooltip = false;
            select.dispatchImmediately("hit");
            select.hideTooltip();

            customRegionGroup = false;

            // reset true click events controller
            iMaps.maps[id].activeStateControl = true;
          }

          //then hilight the first one
          group = elID.split(",");
          group.forEach(function (rxid, indx) {
            select = series[i].getPolygonById(rxid.trim());

            if (typeof select !== "undefined" && select) {
              if (select.dataItem.dataContext.madeFromGeoData) {
                return;
              }

              // if originalID is not the same as the current originalID from the group, let's ignore it, it could belong to another group
              if (
                select.dataItem.dataContext.originalID !== elID &&
                !customRegionGroup
              ) {
                return;
              }

              //the first one, let's trigger the tooltip and the hit
              if (indx === 0 && showTooltip) {
                triggered = true;
                if (forceFixedTooltip) {
                  defaultTooltipPosition =
                    typeof select !== "undefined" &&
                    typeof select.tooltipPosition !== "undefined"
                      ? select.tooltipPosition
                      : "hover";
                  select.tooltipPosition = "fixed";

                  // experimental code to check if on hit tooltip remains
                  defaultTooltipShowOn = select.showTooltipOn;
                  select.showTooltipOn = "always";

                  iMaps.maps[id].activeStateControl = true;

                  select.tooltipPosition = defaultTooltipPosition;
                  select.showTooltipOn = defaultTooltipShowOn;

                  select.selectedFromGroup = true;
                } else {
                  select.isHover = true;
                }
              }

              // if we have the active state, use it instead
              if (
                data.regionActiveState &&
                im.bool(data.regionActiveState.enabled)
              ) {
                select.setState("active");
              } else {
                select.setState("highlight");
              }

              selected.push(select);
            }
          });
        } else {
          // individual region
          select = series[i].getPolygonById(elID);

          if (typeof select !== "undefined" && select) {
            let timeout = 0;
            //check if globe to rotate
            if (data.projection === "Orthographic") {
              let map = iMaps.maps[id].map;
              map.animate(
                {
                  property: "deltaLongitude",
                  to: -select.longitude,
                },
                500,
                am4core.ease.linear
              );
              map.animate(
                {
                  property: "deltaLatitude",
                  to: -select.latitude,
                },
                550,
                am4core.ease.linear
              );
              timeout = 550;
            }

            setTimeout(
              function (select) {
                console.log(select.dataItem.dataContext);

                if (
                  forceFixedTooltip &&
                  select.dataItem.dataContext &&
                  select.dataItem.dataContext.action !== "igm_display_map"
                ) {
                  defaultTooltipPosition =
                    typeof select !== "undefined" &&
                    typeof select.tooltipPosition !== "undefined"
                      ? select.tooltipPosition
                      : "hover";
                  select.tooltipPosition = "fixed";
                  // experimental code to check if on hit tooltip remains
                  defaultTooltipShowOn = select.showTooltipOn;
                  select.showTooltipOn = "always";
                  select.dispatchImmediately("hit");
                  triggered = true;
                  select.tooltipPosition = defaultTooltipPosition;
                  select.showTooltipOn = defaultTooltipShowOn;
                  selected.push(select);
                }
                // if action is set to display map it will zoom and we don't need to show tooltip
                else if (
                  select.dataItem.dataContext &&
                  select.dataItem.dataContext.action === "igm_display_map"
                ) {
                  select.dispatchImmediately("hit");
                  select.hideTooltip();
                  triggered = true;
                  selected.push(select);
                } else {
                  select.dispatchImmediately("hit");
                  triggered = true;
                  selected.push(select);
                }
              },
              timeout,
              select
            );
          }
        }
      }
      // imageSeries
      if (series[i].mapImages) {
        // mutiple markers
        if (elID.includes(",")) {
          // hilight
          group = elID.split(",");
          group.forEach(function (rxid, indx) {
            select = series[i].getImageById(rxid);

            if (typeof select !== "undefined" && select) {
              // show series if it's hidden
              series[i].show();

              if (forceFixedTooltip) {
                defaultTooltipShowOn = select.showTooltipOn;
                select.showTooltipOn = "always";

                defaultTooltipPosition =
                  typeof select && typeof select.tooltipPosition !== "undefined"
                    ? select.tooltipPosition
                    : "hover";
                select.tooltipPosition = "fixed";
                select.dispatchImmediately("hit");
                triggered = true;
                select.tooltipPosition = defaultTooltipPosition;
                select.showTooltipOn = defaultTooltipShowOn;
                selected.push(select);
              } else {
                select.dispatchImmediately("hit");
                triggered = true;
                selected.push(select);
              }
            }
          });
        }
        // single marker
        else {
          let selectMarker = series[i].getImageById(elID);
          if (selectMarker) {
            // show series if it's hidden
            series[i].show();

            let timeoutM = 0;
            //check if globe to rotate
            if (data.projection === "Orthographic") {
              let map = iMaps.maps[id].map;
              map.animate(
                {
                  property: "deltaLongitude",
                  to: -selectMarker.longitude,
                },
                500,
                am4core.ease.linear
              );
              map.animate(
                {
                  property: "deltaLatitude",
                  to: -selectMarker.latitude,
                },
                550,
                am4core.ease.linear
              );
              timeoutM = 550;
            }

            setTimeout(function () {
              if (forceFixedTooltip) {
                defaultTooltipPosition = selectMarker.tooltipPosition;
                defaultTooltipShowOn = selectMarker.showTooltipOn;
                selectMarker.tooltipPosition = "fixed";
                selectMarker.showTooltipOn = "always";
                selectMarker.isHover = true;
                selectMarker.isActive = true;
                // to also show tooltip when using the select method
                selectMarker.dispatchImmediately("hit");
                selectMarker.setState("active");
                selectMarker.children.each(function (child) {
                  //if(child.className === 'Circle'){
                  child.showTooltip(0);
                  //}
                });
                triggered = true;
                selectMarker.tooltipPosition = defaultTooltipPosition;
                selectMarker.showTooltipOn = defaultTooltipShowOn;
                selected.push(selectMarker);
              } else {
                selectMarker.dispatchImmediately("hit");
                triggered = true;
                selected.push(selectMarker);
              }
            }, timeoutM);
          }
        }
      }
    }
  }

  im.maps[id].selected = selected;
  return select;
};

iMapsManager.setupRangeHeatMap = function (series, id, data) {
  var im = this,
    regions = data.regions,
    markers = data.roundMarkers,
    reordered,
    seriesType = im.getTargetSeriesType(series);

  if (seriesType === "MapImageSeries") {
    if (
      !Array.isArray(data.heatMapMarkers.range) ||
      data.heatMapMarkers.range.length === 0
    ) {
      return;
    }

    // reorder ranges by value
    reordered = data.heatMapMarkers.range.slice(0);
    reordered.sort(function (a, b) {
      if (isNaN(a.rule)) {
        var x = a.rule.toLowerCase();
        var y = b.rule.toLowerCase();
        return x < y ? -1 : x > y ? 1 : 0;
      } else {
        return parseFloat(a.rule) - parseFloat(b.rule);
      }
    });

    if (Array.isArray(markers) && markers.length > 0) {
      markers.forEach(function (entry, index) {
        if (typeof entry[data.heatMapMarkers.source] === "undefined") {
          return;
        }

        // default to correct value reference, since value will be only numeric
        if (data.heatMapMarkers.source === "value") {
          data.heatMapMarkers.source = "val";
        }

        var val = entry[data.heatMapMarkers.source];
        var start;

        if (!isNaN(val)) {
          val = parseFloat(val);
        }

        reordered.forEach(function (ruleData, index) {
          if (isNaN(ruleData.rule)) {
            start = ruleData.rule.trim(); // if it's not a number

            if (val == start) {
              entry.fill = ruleData.fill;
              entry.radius = parseFloat(ruleData.radius);
            }
          } else {
            start = parseFloat(ruleData.rule); // if it's a number

            if (val >= start) {
              entry.fill = ruleData.fill;
              entry.radius = parseFloat(ruleData.radius);
            }
          }
        });
      });
    }
  } else if (seriesType === "MapPolygonSeries") {
    if (
      !Array.isArray(data.heatMapRegions.range) ||
      data.heatMapRegions.range.length === 0
    ) {
      return;
    } // reorder ranges by value

    reordered = data.heatMapRegions.range.slice(0);
    reordered.sort(function (a, b) {
      if (isNaN(a.rule)) {
        var x = a.rule.toLowerCase();
        var y = b.rule.toLowerCase();
        return x < y ? -1 : x > y ? 1 : 0;
      } else {
        return parseFloat(a.rule) - parseFloat(b.rule);
      }
    });

    if (Array.isArray(regions) && regions.length > 0) {
      if (!isNaN(data.heatMapRegions.source)) {
        data.heatMapRegions.source = parseInt(data.heatMapRegions.source);
      }

      regions.forEach(function (entry, index) {
        if (typeof entry[data.heatMapRegions.source] === "undefined") {
          return;
        }

        if (data.heatMapRegions.source === "value") {
          data.heatMapRegions.source = "val";
        }

        var val = entry[data.heatMapRegions.source];
        var start;

        if (!isNaN(val)) {
          val = parseFloat(val);
        }

        reordered.forEach(function (ruleData, index) {
          if (isNaN(ruleData.rule)) {
            start = ruleData.rule.trim(); // if it's a number

            if (val == start) {
              entry.fill = ruleData.fill;
            }
          } else {
            start = parseFloat(ruleData.rule); // if it's a number

            if (val >= start) {
              entry.fill = ruleData.fill;
            }
          }
        });
      });
    }
  }
};
/**
 *
 * @param {object} series
 * @param {int} id - parent map id
 * @param {object} data - current map data
 */

iMapsManager.setupHeatMap = function (series, id, data) {
  var im = this,
    map = im.maps[id].map,
    heatLegend,
    legendLabel,
    legendContainer,
    minRange,
    maxRange,
    target,
    minProp,
    maxProp,
    propTargets,
    dataSource,
    seriesType = im.getTargetSeriesType(series); // setup target

  if (seriesType === "MapImageSeries") {
    if (
      typeof data.heatMapMarkers.type !== "undefined" &&
      data.heatMapMarkers.type === "range"
    ) {
      im.setupRangeHeatMap(series, data.id, data);
      return;
    }

    target = series.mapImages.template.children.values[0];
    propTargets = ["fill", "radius"];
    dataSource = data.heatMapMarkers;
  } else if (seriesType === "MapPolygonSeries") {
    if (
      typeof data.heatMapRegions.type !== "undefined" &&
      data.heatMapRegions.type === "range"
    ) {
      im.setupRangeHeatMap(series, data.id, data);
      return;
    }

    target = series.mapPolygons.template;
    propTargets = ["fill"];
    dataSource = data.heatMapRegions;
  } else {
    return;
  }

  // make sure we read data form the correct property
  series.dataFields.value = dataSource.source;

  if (!Array.isArray(propTargets)) {
    propTargets = [propTargets];
  }

  propTargets.map(function (prop) {
    // setup min/max sources
    if (prop === "fill") {
      minProp = dataSource.minColor;
      maxProp = dataSource.maxColor;
    } else if (prop === "radius") {
      minProp = dataSource.minRadius;
      maxProp = dataSource.maxRadius;
    }

    series.heatRules.push({
      property: prop,
      target: target,
      min: minProp,
      max: maxProp,
    });
  });

  if (im.bool(dataSource.legend)) {
    legendContainer = map.createChild(am4core.Container);
    legendContainer.align =
      typeof dataSource.legendAlign !== "undefined"
        ? dataSource.legendAlign
        : "right";
    legendContainer.valign =
      typeof dataSource.legendValign !== "undefined"
        ? dataSource.legendValign
        : "bottom";
    legendContainer.userClassName = "mapLegendContainer";
    legendContainer.marginRight = am4core.percent(4);
    //legendContainer.parent = map;

    if (window.innerWidth <= 780) {
      legendContainer.width = am4core.percent(40);
    } else {
      legendContainer.width = am4core.percent(25);
    }

    // legend title
    if (typeof dataSource.label !== "undefined" && dataSource.label !== "") {
      legendLabel = legendContainer.createChild(am4core.Label);
      legendLabel.text = dataSource.label;
      legendLabel.horizontalCenter = "left";
      legendLabel.verticalCenter = "top";

      legendLabel.fontSize = 12;
      legendLabel.paddingBottom = 40;

      if (window.innerWidth <= 780) {
        legendLabel.fontSize = 10;
        legendLabel.paddingBottom = 48;
      } else {
        legendLabel.fontSize = 12;
        legendLabel.paddingBottom = 48;
      }

      legendLabel.nonScaling = false;
      legendLabel.clickable = false;
      legendLabel.focusable = false;
      legendLabel.hoverable = false;
    }

    //legendLabel.parent = heatLegend;
    //label.insertAfter(heatLegend.valueAxis);
    //heatLegend.valueAxis.title.text = 'test title';

    heatLegend = legendContainer.createChild(am4maps.HeatLegend);
    heatLegend.series = series;
    heatLegend.align = "right";
    heatLegend.valign = "bottom";
    heatLegend.width = am4core.percent(100);
    //heatLegend.marginRight = am4core.percent(4);
    heatLegend.minValue = 0;
    heatLegend.maxValue = 99999999999999; // Set up custom heat map legend labels using axis ranges

    minRange = heatLegend.valueAxis.axisRanges.create();
    minRange.value = heatLegend.minValue;
    minRange.label.text = dataSource.minLabel;
    maxRange = heatLegend.valueAxis.axisRanges.create();
    maxRange.value = heatLegend.maxValue;
    maxRange.label.text = dataSource.maxLabel;

    minRange.label.fontSize = 12;
    maxRange.label.fontSize = 12;

    if (window.innerWidth <= 780) {
      minRange.label.fontSize = 10;
      maxRange.label.fontSize = 10;
    } else {
      minRange.label.fontSize = 12;
      maxRange.label.fontSize = 12;
    }

    // Blank out internal heat legend value axis labels
    heatLegend.valueAxis.renderer.labels.template.adapter.add(
      "text",
      function () {
        return "";
      }
    );
  }
};

iMapsManager.drillTo = function (id, ev, currentRegion, customMap) {
  var im = iMapsManager,
    map = im.maps[id].map,
    data = im.maps[id].data,
    allCurrentSeries = iMapsManager.maps[id].series,
    baseSeries = iMapsManager.maps[id].baseSeries,
    allBaseSeries = iMapsManager.maps[id].allBaseSeries,
    bgSeries = im.maps[id].backgroundSeries,
    opacity,
    i,
    len;

  (opacity =
    typeof igmDrilldownBaseMapOpacity !== "undefined"
      ? igmDrilldownBaseMapOpacity
      : 0.3),
    // hide all others except this one and baseSeries
    // if were opening a custom map
    (customMap = customMap || false);

  // hide or fade series
  for (i = 0, len = allCurrentSeries.length; i < len; i++) {
    if (baseSeries.includes(allCurrentSeries[i])) {
      // let's check if new option to keep base map is enabled
      if (
        typeof data.alwaysKeepBase === "undefined" ||
        !im.bool(data.alwaysKeepBase)
      ) {
        allCurrentSeries[i].opacity = opacity;

        // hide image background
        bgSeries.opacity = opacity;
      }
    } else if (allBaseSeries.includes(allCurrentSeries[i])) {
      // let's check if new option to keep base map is enabled
      if (
        typeof data.alwaysKeepBase === "undefined" ||
        !im.bool(data.alwaysKeepBase)
      ) {
        allCurrentSeries[i].hide();
      }
    } else {
      allCurrentSeries[i].hide();
    }
  }

  for (i = 0, len = currentRegion.length; i < len; i++) {
    currentRegion[i].show();
  }
  // is drilling
  iMapsManager.maps[id].isDrilling = true;

  // zoom to region - it will only work when zoom is enabled so that controls exist
  if (!ev.zooming) {
    if (customMap) {
      console.log("drill to specific map");
      im.zoomToMap(ev, currentRegion);
      return;
    }

    im.zoomToRegion(ev, id);
  }
};

iMapsManager.drillDown = function (id, ev) {
  var im = iMapsManager,
    mapName = iMapsRouter.iso2cleanName(ev.target.dataItem.dataContext.id, id),
    targetID = ev.target.dataItem.dataContext.id,
    allCurrentSeries = iMapsManager.maps[id].series,
    clicked = ev.target.dataItem.dataContext,
    currentRegion,
    customMap = false,
    baseSeries = iMapsManager.maps[id].baseSeries,
    allBaseSeries = iMapsManager.maps[id].allBaseSeries,
    i,
    len,
    checkSeries = [],
    seriesExists = false;
  console.log("Map Name:", mapName);
  console.log("Available Series:", iMapsManager.maps[id].seriesIndex);
  console.log("Available Series by ID: ", iMapsManager.maps[id].seriesById); // check if geofile info exists or return if the id is numeric

  if (!mapName || !isNaN(targetID)) {
    // well, let's only return if it's not showing another map
    // code needs better logic...
    if (
      _typeof(clicked) === undefined ||
      clicked.action !== "igm_display_map"
    ) {
      return false;
    }
  }

  if (ev.target.polygon) {
    // what we need to check
    checkSeries.push(mapName); // check if series exists for this map

    checkSeries.forEach(function (ser) {
      if (
        _typeof(clicked) !== undefined &&
        clicked.action == "igm_display_map"
      ) {
        console.log("Display custom map " + clicked.content);

        if (
          Array.isArray(
            iMapsManager.maps[id].seriesById[parseInt(clicked.content)]
          )
        ) {
          seriesExists = true;
          currentRegion =
            iMapsManager.maps[id].seriesById[parseInt(clicked.content)];
          customMap = true;
        }
      } else {
        if (Array.isArray(iMapsManager.maps[id].seriesIndex[ser])) {
          seriesExists = true;
          currentRegion = iMapsManager.maps[id].seriesIndex[ser];
        }
      }
    });

    if (seriesExists) {
      iMapsManager.drillTo(id, ev, currentRegion, customMap);
      iMapsManager.maps[id].drilledTo = currentRegion[0].mapID;
    } else {
      // if the target is part of current series, do nothing
      if (currentRegion === ev.target.series) {
        iMapsManager.maps[id].isDrilling = false;
        return;
      }
      // if target is base series, show it
      if (baseSeries.includes(ev.target.series)) {
        iMapsManager.maps[id].isDrilling = false;
        iMapsManager.maps[id].drilledTo = false;

        // hide all except baseSeries
        for (i = 0, len = allCurrentSeries.length; i < len; i++) {
          if (allBaseSeries.includes(allCurrentSeries[i])) {
            allCurrentSeries[i].show();
          } else {
            allCurrentSeries[i].hide();
          }
        }
      }
    }
  }
};
/*HELPERS*/

iMapsManager.getSelected = function (id) {
  var im = this,
    map = im.maps[id],
    selected = map.selected || false,
    multiple = [];

  if (selected) {
    if (Array.isArray(selected)) {
      selected.forEach(function (sel) {
        multiple.push(sel.dataItem.dataContext);
      });
      return multiple;
    }

    return selected.dataItem.dataContext;
  } else {
    return false;
  }
};

iMapsManager.getHovered = function (id) {
  var im = this,
    map = im.maps[id],
    hovered = map.hovered || false,
    multiple = [];

  if (hovered) {
    if (Array.isArray(hovered)) {
      hovered.forEach(function (sel) {
        multiple.push(sel.dataItem.dataContext);
      });
      return multiple;
    }
  } else {
    return false;
  }
};

iMapsManager.getHighlighted = function (id) {
  var im = this,
    map = im.maps[id],
    highlighted = map.highlighted || false,
    multiple = [];

  if (highlighted) {
    if (Array.isArray(highlighted)) {
      highlighted.forEach(function (sel) {
        multiple.push(sel.dataItem.dataContext);
      });
      return multiple;
    }

    return highlighted.dataItem.dataContext;
  } else {
    return false;
  }
};

iMapsManager.clearSelected = function (id, keepThis, skipReset) {
  var im = this,
    map = im.maps[id],
    selected = map.selected || [];

  // to keep the state of this element
  keepThis = keepThis || false;
  skipReset = skipReset || false;

  if (Array.isArray(selected) && selected.length > 0) {
    selected.forEach(function (polygon, index) {
      if (
        polygon !== keepThis &&
        typeof polygon === "object" &&
        typeof polygon.isHover !== "undefined"
      ) {
        polygon.isHover = false;
        polygon.isActive = false;
        polygon.isGroupActive = false;

        // there were some issues on mobile tap (singleHit) where the tooltip woudl disappear,
        // so we added this check before hiding the tooltip
        if (!keepThis) {
          polygon.hideTooltip(0); // needed to hide tooltip
        }

        if (polygon.selectedFromGroup) {
          polygon.hideTooltip(0); // needed to hide tooltip when group was selected
          polygon.selectedFromGroup = false;
        }

        polygon.setState("default");
      }
    });
    selected = [];
  }

  if (!keepThis && typeof iMapsActions !== "undefined") {
    if (!skipReset) {
      //iMapsActions.resetActions(id);
    }
    map.selected = [];
  } else {
    map.selected = [keepThis];
  }

  return map.selected;
};

iMapsManager.clearHighlighted = function (id) {
  var im = this,
    map = im.maps[id],
    highlighted = map.highlighted || [];

  if (Array.isArray(highlighted) && highlighted.length > 0) {
    highlighted.forEach(function (polygon, index) {
      polygon.isHover = false;
      polygon.isActive = false;
      polygon.setState("default");
    });
    highlighted = [];
  }

  return highlighted;
};
/*
 * setup hover events
 * id - id of the map
 * eID - hovered element ID
 * forcedFixedTooltip - if we should force the tooltip to be fixed or not
 */

iMapsManager.hover = function (id, eID, forceFixedTooltip) {
  var im = this,
    map = im.maps[id],
    data = map.data,
    series = map.series,
    hovered = map.hovered || [],
    hover,
    group,
    defaultShowTooltipOn,
    defaultTooltipPosition; // map sure it's string

  if (Number.isInteger(eID)) {
    eID = eID.toString();
  } // Force fixed position?

  if (typeof forceFixedTooltip === "undefined") {
    forceFixedTooltip = true;
  }

  iMapsManager.clearHovered(id);
  hovered = [];

  if (Array.isArray(series)) {
    for (var i = 0, len = series.length; i < len; i++) {
      // regionSeries
      if (series[i].mapPolygons) {
        // multiple
        // check if group
        if (eID.includes(",")) {
          // foreach code
          group = eID.split(",");
          group.forEach(function (rxid, indx) {
            // single
            hover = series[i].getPolygonById(rxid.trim());

            if (hover) {
              if (forceFixedTooltip) {
                defaultTooltipPosition =
                  typeof hover.tooltipPosition !== "undefined"
                    ? hover.tooltipPosition
                    : "fixed";
                defaultShowTooltipOn =
                  typeof hover.showTooltipOn !== "undefined"
                    ? hover.showTooltipOn
                    : "hover";
                hover.tooltipPosition = "fixed";
                hover.showTooltipOn = "always";
                hovered.push(hover);
                hover.dispatchImmediately("over");
                hover.isHover = true;
                hover.tooltipPosition = defaultTooltipPosition;
                hover.showTooltipOn = defaultShowTooltipOn;
              } else {
                hovered.push(hover);
                hover.dispatchImmediately("over");
                hover.isHover = true;
              }
            }
          });
        } else {
          // single region
          hover = series[i].getPolygonById(eID);

          if (hover) {
            if (forceFixedTooltip) {
              defaultTooltipPosition = hover.tooltipPosition;
              hover.tooltipPosition = "fixed";
              hovered = [hover];
              hover.dispatchImmediately("over");
              hover.isHover = true;
              hover.tooltipPosition = defaultTooltipPosition;
            } else {
              hovered = [hover];
              hover.dispatchImmediately("over");
              hover.isHover = true;
            }
          }
        }
      }
      // imageSeries
      if (series[i].mapImages) {
        // multiple markers
        if (eID.includes(",")) {
          // foreach code
          group = eID.split(",");
          group.forEach(function (rxid, indx) {
            hover = series[i].getImageById(rxid);

            if (hover) {
              if (forceFixedTooltip) {
                defaultTooltipPosition = hover.tooltipPosition;
                defaultShowTooltipOn = hover.showTooltipOn;
                hover.tooltipPosition = "fixed";
                hover.showTooltipOn = "always";
                hovered = [hover];
                hover.dispatchImmediately("over");
                hover.isHover = true;
                hover.setState("hover");
                hover.children.each(function (child) {
                  if (child.className === "Circle") {
                    child.showTooltip(0);
                  }
                });
                hover.tooltipPosition = defaultTooltipPosition;
                hover.showTooltipOn = defaultShowTooltipOn;
              } else {
                hovered = [hover];
                hover.dispatchImmediately("over");
                hover.isHover = true;
                hover.setState("hover");
              }
            }
          });
        }
        // single marker
        else {
          hover = series[i].getImageById(eID);

          if (hover) {
            if (forceFixedTooltip) {
              defaultTooltipPosition = hover.tooltipPosition;
              hover.tooltipPosition = "fixed";
              hovered = [hover];
              hover.dispatchImmediately("over");
              hover.isHover = true;
              hover.setState("hover");
              hover.children.each(function (child) {
                if (child.className === "Circle") {
                  child.showTooltip(0);
                }
              });
              hover.tooltipPosition = defaultTooltipPosition;
            } else {
              hovered = [hover];
              hover.dispatchImmediately("over");
              hover.isHover = true;
              hover.setState("hover");
            }
          }
        }
      }
    }
  }

  map.hovered = hovered;
  return hover;
};

iMapsManager.clearHovered = function (id, eID) {
  var im = this,
    map = im.maps[id],
    hovered = map.hovered || false,
    series = map.series,
    hover;
  eID = eID || false;

  if (eID) {
    if (Array.isArray(series)) {
      for (var i = 0, len = series.length; i < len; i++) {
        // regionSeries - specific region
        if (series[i].mapPolygons) {
          hover = series[i].getPolygonById(eID);

          if (hover) {
            hover.dispatchImmediately("out");
            hover.isHover = false;
          }
        }

        // imageSeries - specific marker
        if (series[i].mapImages) {
          hover = series[i].getImageById(eID);

          if (hover) {
            hover.isHover = false;
            hover.setState("default");
            hover.dispatchImmediately("out");
          }
        }
      }
    }
  }
  // all of the regions or markers
  else {
    if (hovered) {
      hovered.forEach(function (hov) {
        hov.dispatchImmediately("out");
        if (typeof hov.isGroupActive === "undefined" || !hov.isGroupActive) {
          hov.setState("default");
          hov.isHover = false;
        }
        if (typeof hov.children !== "undefined") {
          hov.children.each(function (child) {
            if (child.className === "Circle") {
              child.hideTooltip(0);
            }
          });
        }
      });
      map.hovered = [];
      return true;
    }
  }

  return false;
};
/**
 * id - map ID
 * elID - element to highlight
 */

iMapsManager.highlight = function (id, elID) {
  var im = this,
    map = im.maps[id],
    series = map.series,
    select,
    highlighted = map.highlighted || [],
    group; // map sure it's string

  if (Number.isInteger(elID)) {
    elID = elID.toString();
  } //iMapsManager.clearSelected(id);

  if (Array.isArray(series)) {
    for (var i = 0, len = series.length; i < len; i++) {
      // regionSeries
      if (series[i].mapPolygons) {
        // check if group
        if (elID.includes(",")) {
          group = elID.split(",");
          group.forEach(function (rxid, indx) {
            select = series[i].getPolygonById(rxid.trim());

            if (typeof select !== "undefined" && select) {
              if (select.dataItem.dataContext.madeFromGeoData) {
                return;
              }

              select.setState("highlight");
              highlighted.push(select);
            }
          });
        } else {
          select = series[i].getPolygonById(elID);

          if (typeof select !== "undefined" && select) {
            select.setState("highlight");
            highlighted.push(select);
          }
        }
      } // imageSeries

      if (series[i].mapImages) {
        if (elID.includes(",")) {
          group = elID.split(",");
          group.forEach(function (rxid, indx) {
            select = series[i].getImageById(rxid);

            if (typeof select !== "undefined" && select) {
              select.setStateOnChildren = true;
              select.setState("highlight");
              highlighted.push(select);
            }
          });
        } else {
          select = series[i].getImageById(elID);

          if (typeof select !== "undefined" && select) {
            select.setStateOnChildren = true;
            select.setState("highlight");
            highlighted.push(select);
          }
        }
      }
    }
  }

  map.highlighted = highlighted;
  return select;
};

iMapsManager.getTargetSeriesType = function (el) {
  var className = el.className;
  return className;
};
/**
 * Setups clustered series based on coordinate values from data
 */

iMapsManager.setupClusters = function (data, id, overlay) {
  var im = this,
    map = im.maps[id],
    series = [],
    markerSeries,
    tempData = {},
    biasLevels = [],
    zoomLevels = [],
    biasSteps = 4,
    i = 0,
    prevBias = parseFloat(data.clusterMarkers.maxBias),
    maxZoomLevel = parseFloat(data.clusterMarkers.zoomLevel) || 20,
    tooltipTemplate =
      typeof data.clusterMarkers.tooltipTemplate !== "undefined"
        ? data.clusterMarkers.tooltipTemplate
        : false;
  overlay = overlay || false;
  var currentMap = overlay ? overlay : id;

  while (i <= biasSteps) {
    biasLevels.push(prevBias);
    prevBias = prevBias / 2;
    zoomLevels.push(maxZoomLevel);

    // make sure the last one is set to 1, otherwise it will be hidden
    maxZoomLevel = i == 3 ? 1 : Math.ceil(maxZoomLevel / 2);
    i++;
  }

  // reverse array to match detail level
  zoomLevels.reverse().pop();
  biasLevels.pop();

  if (typeof map.clusterSeries[currentMap] === "undefined") {
    map.clusterSeries[currentMap] = {
      zoomLevels: {},
      overlay: overlay,
    };
  }

  if (Array.isArray(data.roundMarkers)) {
    biasLevels.forEach(function (item, index) {
      series = geocluster(
        data.roundMarkers,
        item,
        data.markerDefaults,
        tooltipTemplate
      );
      tempData = Object.assign({}, data);
      tempData.roundMarkers = series;
      markerSeries = im.pushRoundMarkerSeries(id, tempData);
      markerSeries.name = tempData.title || "Map";
      markerSeries.hiddenInLegend = true; // set as cluster to be identified later in select method

      markerSeries.isCluster = true;
      map.clusterSeries[currentMap].zoomLevels[zoomLevels[index]] =
        markerSeries;

      if (index === 0 && !overlay) {
        // this somehow interfeers with the clusters after home button is clicked
        // im.maps[id].allBaseSeries.push(markerSeries);
      }

      // hide all series except the first
      //if (index > 0 ) {
      markerSeries.hidden = true;
      //}
    });
  }

  return true;
};

/**
 * triggers when map is ready
 * @param {int} id of the map
 */
iMapsManager.triggerOnReady = function (id, data) {
  let isCustom = data.map == "custom" ? true : false;
  let urlParams = new URLSearchParams(window.location.search);
  let myParam = urlParams.get("mregion");
  if (myParam) {
    //if it's a custom map, do nothing, we will trigger it on the map appeared event
    if (!isCustom) {
      iMapsManager.select(id, myParam, true, true);
    }
  }
};

/**
 * triggers when map is ready
 * @param {int} id of the map
 */
iMapsManager.triggerOnAppeared = function (id, data) {
  let isCustom = data.map == "custom" ? true : false;
  let urlParams = new URLSearchParams(window.location.search);
  let myParam = urlParams.get("mregion");
  if (myParam) {
    //if it's a custom map, do nothing, we will trigger it on the map appeared event
    if (isCustom) {
      setTimeout(function () {
        iMapsManager.select(id, myParam, true, true);
      }, 500);
    }
  }
};

/**
 * Adds a new region Series currently not loaded, adding the script to the body of the page and creating a new series after
 * id - map id to attach series
 * dataContent - object with data that would tipically be a polygon dataContent, like name and id
 * config - config object for the new series being added
 *
 * @return newSeries - the new created series object
 */

iMapsManager.addGeoFileSeries = function (id, dataContext, data) {
  var newSeries,
    geoFiles = iMapsRouter.getGeoFiles(dataContext);
  var scriptPromise = new Promise(function (resolve, reject) {
    var script = document.createElement("script");
    document.body.appendChild(script);
    script.onload = resolve;
    script.onerror = reject;
    script.async = true;
    script.src = geoFiles.src;
  });
  scriptPromise.then(function () {
    var data = {
      title: geoFiles.title,
      map: geoFiles.map,
      regions: [],
      config: data, // not working, we changed the config to be at parent level with data
    };
    iMapsManager.maps[id].seriesIndex[geoFiles.map] = [];
    newSeries = iMapsManager.pushRegionSeries(id, data);
    iMapsManager.maps[id].seriesIndex[geoFiles.map].push(newSeries);
    return newSeries;
  });
  return false;
};

iMapsManager.handleInfoBox = function (id) {
  var im = this,
    map = im.maps[id].map,
    events = ["ready", "mappositionchanged", "zoomlevelchanged"],
    container = document.getElementById("map_visual_info"),
    coordinatesc = document.getElementById("map_click_events_coordinates"),
    series = im.maps[id].series;

  if (container) {
    iMapsManager.populateInfo(id, container); // zoom, etc

    events.forEach(function (event) {
      map.events.on(
        event,
        function (ev) {
          iMapsManager.populateInfo(id, container);
        },
        this
      );
    });
  }

  if (coordinatesc) {
    map.events.on(
      "hit",
      function (ev) {
        var coordinates = map.svgPointToGeo(ev.svgPoint);
        var lat = Number(coordinates.latitude).toFixed(6);

        var _long = Number(coordinates.longitude).toFixed(6); // latitude

        var latEl = document.createElement("div");
        var latLabelEl = document.createElement("span");
        var latValueEl = document.createElement("span");
        latLabelEl.classList.add("info--unselectable");
        latValueEl.classList.add("map_clicked_lat");
        latLabelEl.innerHTML = "LAT: ";
        latValueEl.innerHTML = lat;
        latEl.appendChild(latLabelEl);
        latEl.appendChild(latValueEl); // longitude

        var longEl = document.createElement("div");
        var longLabelEl = document.createElement("span");
        var longValueEl = document.createElement("span");
        longLabelEl.classList.add("info--unselectable");
        longValueEl.classList.add("map_clicked_long");
        longLabelEl.innerHTML = "LON: ";
        longValueEl.innerHTML = _long;
        longEl.appendChild(longLabelEl);
        longEl.appendChild(longValueEl);
        coordinatesc.innerHTML = "";
        coordinatesc.appendChild(latEl);
        coordinatesc.appendChild(longEl);
        coordinatesc.parentElement.style.display = "block";
        var event = new CustomEvent("mapPointClicked", {
          detail: {
            latitude: lat,
            longitude: _long,
          },
        });
        document.dispatchEvent(event);
      },
      this
    );
  }
};

iMapsManager.populateInfo = function (id, container) {
  var im = this,
    map = im.maps[id].map,
    info = "";
  info +=
    "Zoom Level: " + parseFloat(Number(map.zoomLevel).toFixed(2)) + "<br>";
  info +=
    "Center Coordinates: <br>" +
    "<span class='info--unselectable'>LAT </span><span>" +
    Number(map.zoomGeoPoint.latitude).toFixed(6) +
    "</span>";
  info +=
    "<br><span class='info--unselectable'>LONG </span><span>" +
    Number(map.zoomGeoPoint.longitude).toFixed(6) +
    "</span><br>";
  container.innerHTML = info;

  let centerData = {
    zoom: parseFloat(Number(map.zoomLevel).toFixed(2)),
    lat: Number(map.zoomGeoPoint.latitude).toFixed(6),
    long: Number(map.zoomGeoPoint.longitude).toFixed(6),
  };

  container.setAttribute("data-visual", JSON.stringify(centerData));
};

iMapsManager.hideAllSeries = function (id, keepBase) {
  id = parseInt(id);

  if (!id) {
    return;
  }

  keepBase = keepBase || false;
  var map = iMaps.maps[id];
  var baseRegionSeries = map.baseRegionSeries;
  var groupedSeries = map.groupedBaseRegionSeries;
  var allbaseSeries = map.allBaseSeries;

  for (var index = 0; index < map.series.length; index++) {
    var serie = map.series[index];

    //if (baseRegionSeries !== serie && !groupedSeries.includes(serie)) {
    // we don't need to check if it's not the base series at this point

    if (!groupedSeries.includes(serie)) {
      // in case we need to check also if we keep base series
      if (!keepBase || (keepBase && !allbaseSeries.includes(serie))) {
        serie.hide();
      }
    }
  }
};

iMapsManager.showAllSeries = function (id) {
  id = parseInt(id);

  if (!id) {
    return;
  }

  var map = iMaps.maps[id];

  for (var index = 0; index < map.series.length; index++) {
    var serie = map.series[index];

    if (typeof serie.isCluster === "undefined" || serie.isCluster === false) {
      serie.show();
    }
  }
};
/** Util function to return boolean value of string */

iMapsManager.bool = function (string) {
  var bool =
    Number(string) === 0 || string === "false" || typeof string === "undefined"
      ? false
      : true;
  return bool;
};

iMapsManager.isJSON = function (str) {
  try {
    JSON.parse(str);
  } catch (e) {
    return false;
  }

  return true;
};
/* Closest Polyfill */

if (!Element.prototype.matches) {
  Element.prototype.matches =
    Element.prototype.msMatchesSelector ||
    Element.prototype.webkitMatchesSelector;
}

if (!Element.prototype.closest) {
  Element.prototype.closest = function (s) {
    var el = this;

    do {
      if (el.matches(s)) return el;
      el = el.parentElement || el.parentNode;
    } while (el !== null && el.nodeType === 1);

    return null;
  };
}
/** Library for elements
 */

iMapsManager.library = {
  icons: {
    goFullIconPath:
      "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",
    exitFullIconPath:
      "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",
  },
};

iMapsManager.handleExternalZoom = function (id) {
  var mapContainer, mapBox, controls, homeBtn, zoomInBtn, zoomOutBtn;
  mapContainer = document.getElementById("map_wrapper_" + id);
  var im = this,
    data = im.maps[id].data,
    allCurrentSeries = im.maps[id].series,
    allBaseSeries = im.maps[id].allBaseSeries;

  if (!mapContainer) {
    return;
  }

  mapBox = mapContainer.querySelector(".map_box");
  mapContainer.classList.add("map_has_external_controls"); // home button

  homeBtn = document.createElement("div");
  homeBtn.setAttribute("id", "map_home_buttom_" + id);
  homeBtn.setAttribute("data-map-id", id);
  homeBtn.classList.add("map_home_button");
  homeBtn.innerHTML =
    '<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>';
  homeBtn.addEventListener("click", function (ev) {
    var id = this.getAttribute("data-map-id");
    iMaps.maps[id].map.goHome();

    // in case drillDown is enabled, we hide everything else
    if (im.bool(data.drillDownOnClick)) {
      for (var i = 0, len = allCurrentSeries.length; i < len; i++) {
        allCurrentSeries[i].hide(); //map.deltaLongitude = 0;
      }

      for (var ib = 0, lenb = allBaseSeries.length; ib < lenb; ib++) {
        // this is messing the cluster markers on base map
        allBaseSeries[ib].show();
      }

      iMapsManager.maps[id].drilledTo = false;
      iMapsManager.maps[id].isDrilling = false;
    }

    // reset actions
    if (
      typeof iMapsActions !== "undefined" &&
      typeof iMapsActions.resetActions !== "undefined"
    ) {
      iMapsActions.resetActions(id);
    }
  }); // zoom in

  zoomInBtn = document.createElement("div");
  zoomInBtn.setAttribute("id", "map_zoomin_buttom_" + id);
  zoomInBtn.setAttribute("data-map-id", id);
  zoomInBtn.classList.add("map_zoomin_button");
  zoomInBtn.innerHTML = "+";
  zoomInBtn.addEventListener("click", function (ev) {
    var id = this.getAttribute("data-map-id");
    iMaps.maps[id].map.zoomIn();
  }); // zoom out

  zoomOutBtn = document.createElement("div");
  zoomOutBtn.setAttribute("id", "map_zoomout_buttom_" + id);
  zoomOutBtn.setAttribute("data-map-id", id);
  zoomOutBtn.classList.add("map_zoomout_button");
  zoomOutBtn.innerHTML = "-";
  zoomOutBtn.addEventListener("click", function (ev) {
    var id = this.getAttribute("data-map-id");
    iMaps.maps[id].map.zoomOut();
  }); // controls container

  controls = document.createElement("div");
  controls.setAttribute("id", "map_controls_" + id);
  controls.classList.add("map_controls");
  controls.appendChild(homeBtn);
  controls.appendChild(zoomInBtn);
  controls.appendChild(zoomOutBtn); // add buttons

  mapBox.parentNode.insertBefore(controls, mapBox.nextSibling);
};

iMapsManager.toggleFullscreen = function (element) {
  // se não está em tela cheia
  if (!document.fullscreen && !document.webkitIsFullScreen) {
    // solicitar tela cheia
    if (element.requestFullscreen) {
      // default
      element.requestFullscreen();
    } else if (element.mozRequestFullScreen) {
      // Mozilla
      element.mozRequestFullScreen();
    } else if (element.webkitRequestFullscreen) {
      // Chrome e Safari
      element.webkitRequestFullscreen();
    } else if (element.msRequestFullscreen) {
      // Internet Explorer
      element.msRequestFullscreen();
    }
  } else {
    // exit fullscreen
    if (document.exitFullscreen) {
      // default
      document.exitFullscreen();
    } else if (document.mozCancelFullScreen) {
      // Mozilla
      document.mozCancelFullScreen();
    } else if (document.webkitExitFullscreen) {
      // Chrome e Safari
      document.webkitExitFullscreen();
    } else if (document.msExitFullscreen) {
      // Internet Explorer
      document.msExitFullscreen();
    }
    iMapsManager.isFullScreen = false;
  }
};

iMapsManager.isTouchScreendevice = function () {
  return (
    "ontouchstart" in window ||
    navigator.maxTouchPoints > 0 ||
    navigator.msMaxTouchPoints > 0
  );
};

/**
 * This function is same as PHP's nl2br() with default parameters.
 *
 * @param {string} str Input text
 * @param {boolean} replaceMode Use replace instead of insert
 * @param {boolean} isXhtml Use XHTML
 * @return {string} Filtered text
 */

iMapsManager.nl2br = function (str) {
  var replaceStr = "HI<br>HI";
  return (str + "").replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, replaceStr);
};

iMapsManager.isFullScreen = false;
/**
 * Main app file.  Initializes app components.
 */

/**
 * The main app object.
 *
 */

var iMaps = {
  originalData: JSON.parse(JSON.stringify(iMapsData)),
};

iMaps.reset = function () {
  if (iMaps.maps) {
    Object.keys(iMaps.maps).forEach(function (id) {
      iMaps.maps[id].map.dispose();
    });
  }

  iMapsData = iMaps.originalData;
  iMaps.init();
};
/**
 * Initializes the interactiveMaps app
 *
 */

iMaps.init = function (hold) {
  // if map should not render
  if (
    typeof hold === "undefined" &&
    typeof iMapsData.options !== "undefined" &&
    typeof iMapsData.options.hold !== "undefined" &&
    iMapsData.options.hold === "1"
  ) {
    hold = true;
  }

  if (hold) {
    return;
  }

  if (typeof am4core === "undefined") {
    console.log("Map files not loaded properly.");

    let oxygen = document.querySelector(
      ".oxygen-body .map_wrapper .map_render"
    );
    if (oxygen) {
      oxygen.innerHTML =
        '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.';
    }
    return;
  }

  am4core.ready(function () {
    var data;
    am4core.options.autoSetClassName = true;
    am4core.options.classNamePrefix = "imaps";
    am4core.options.commercialLicense = true;
    am4core.options.queue = true;

    if (
      typeof iMapsData.options !== "undefined" &&
      typeof iMapsData.options.lazyLoad !== "undefined" &&
      iMapsData.options.lazyLoad === "1"
    ) {
      am4core.options.onlyShowOnViewport = true;
    } // animated

    if (
      typeof iMapsData.options !== "undefined" &&
      typeof iMapsData.options.animations !== "undefined" &&
      (iMapsData.options.animations === "1" ||
        iMapsData.options.animations === true)
    ) {
      am4core.useTheme(am4themes_animated);
    }

    data = iMapsModel.prepareData(iMapsData.data);
    data.forEach(function (data, index) {
      if (index.disabled) {
        return;
      }

      iMapsManager.init(index);
    });
    iMaps.maps = iMapsManager.maps;
  });
};

iMaps.loadScript = function (url, callback) {
  var script = document.createElement("script");
  script.type = "text/javascript";
  script.src = url;
  script.onreadystatechange = callback;
  script.onload = callback;
  document.head.appendChild(script);
};

iMaps.loadScripts = function (urls, callback) {
  var loadedCount = 0;

  var multiCallback = function multiCallback() {
    loadedCount++;

    if (loadedCount >= urls.length) {
      callback.call(this, arguments);
    }
  };

  urls.forEach(function (url, index) {
    iMaps.loadScript(url, multiCallback);
  });
};

if (
  typeof iMapsData.async !== "undefined" &&
  Array.isArray(iMapsData.async) &&
  iMapsData.async.length > 0
) {
  // we need to load the core file first
  iMaps.loadScript(iMapsData.async[0], function () {
    // then we load the rest - shift() removes the first entry
    iMapsData.async.shift();
    iMaps.loadScripts(iMapsData.async, function () {
      iMaps.init();
    });
  });
} else {
  iMaps.init();
}
