Search

Combos and Time Bar

Combine Nodes

Scroll and pan the time bar to filter chart items by time.

Combos and Time Bar
View live example →

Scroll and pan the time bar to filter items outside the time range.

To use the time bar as a chart filter, update the app state with items in the time bar range. You can then use the items in range to update the chart state.

See also

import React, { useState } from "react";
import { createRoot } from "react-dom/client";

import mapValues from "lodash/mapValues";
import { Chart, TimeBar } from "regraph";

import sourceData from "./data";
import { style } from "@ci/theme/rg/js/storyStyles";

import "@fortawesome/fontawesome-free/css/fontawesome.css";
import "@fortawesome/fontawesome-free/css/solid.css";
import "@ci/theme/rg/css/layout.css";

const Virus = "/public/img/virus.png";

const { hasOwnProperty: has } = Object;

const ransomwareColors = {
  TeslaCrypt: {
    light: style.secondary0.color,
    strong: style.secondary2.color,
  },
  CryptoWall: {
    light: style.colors.blueFaded,
    strong: style.tertiary2.color,
  },
  Locky: {
    light: style.colors.tealFaded,
    strong: style.primary1.color,
  },
};

const shouldFadeNode = ({ data }, key, inRange) => {
  const { type, host } = data;
  if (type === "ip") {
    return !host.some((value) => inRange[value]);
  }
  if (type === "host") {
    return !inRange[key];
  }
  return true;
};

function ComboTimeBar({ items }) {
  const [combos, setCombos] = useState({});
  const [timeBarLoaded, setTimeBarLoaded] = useState(false);
  const [state, setState] = useState({
    inRange: items,
    selection: {},
    layout: { name: "organic", tightness: 5 },
    combine: { properties: ["group"] },
  });

  const setTimeRange = (change) => {
    if (change.items) {
      // the changed state has all items in the current time range
      if (timeBarLoaded) {
        // only set the state after the first time bar load
        // as the first load will include all items
        setState((current) => {
          return { ...current, inRange: change.items };
        });
      }
      setTimeBarLoaded(true);
    }
  };

  const timeBarSelection = (selection) => {
    let selected = [];
    let color = [];
    const selectedKeys = Object.keys(selection);
    let hostSelected = false;
    selectedKeys.forEach((key) => {
      if (has.call(combos, key)) {
        const { type, nodes, combo } = combos[key];
        if (type === "host") {
          // a combo is selected
          if (!hostSelected) {
            selected = [];
            color = [];
          }
          const { group } = combo;
          selected.push(nodes);
          color.push(ransomwareColors[group].strong);
          hostSelected = true;
        } else if (!hostSelected) {
          // a country is selected
          const thisSel = {};
          // build an object for the selection prop
          Object.keys(nodes).forEach((nodeId) => {
            const { host } = nodes[nodeId].data;
            host.forEach((hostId) => {
              const { data: hostData } = items[hostId];
              const { group } = hostData;
              thisSel[group] = thisSel[group] || {};
              thisSel[group][hostId] = true;
            });
          });
          // add the linked hosts to the time bar selection
          Object.keys(thisSel).forEach((group) => {
            const selectionIndex = color.indexOf(ransomwareColors[group].strong);
            // check to see if selection line already exists
            if (selectionIndex > -1) {
              selected[selectionIndex] = { ...selected[selectionIndex], ...thisSel[group] };
            } else {
              selected.push(thisSel[group]);
              color.push(ransomwareColors[group].strong);
            }
          });
        }
      }
    });
    return { selected, color };
  };

  const combineNodesHandler = ({ setStyle, nodes, combo, id }) => {
    const nodeValues = Object.values(nodes);
    const { data, glyphs } = nodeValues[0];
    const { type } = data;
    if (!has.call(combos, id)) {
      // create combos object to refer to when handling selection
      combos[id] = { open: false, nodes, combo, type };
    }
    const { open } = combos[id];
    const { group } = combo;
    const colors = ransomwareColors[group];
    const comboStyle = {
      arrange: "concentric",
      open,
      label: { text: group },
      size: nodeValues.length ** 0.4,
      color: type === "host" ? colors.light : style.colors.offWhite,
      border: type === "host" ? { width: 10, color: colors.strong } : undefined,
      closedStyle: {
        image: type === "ip" ? glyphs[0].image : Virus,
        color: type === "host" ? colors.strong : style.colors.transparent,
        border: {},
        label: [{ text: group, position: "s" }],
      },
    };
    if (type === "host") {
      const hostColors = ransomwareColors[group];
      if (open) {
        comboStyle.color = hostColors.light;
        comboStyle.border = {
          width: 10,
          color: hostColors.strong,
        };
      } else {
        comboStyle.color = hostColors.strong;
      }
    } else if (type === "ip") {
      comboStyle.color = open ? style.colors.offWhite : style.colors.transparent;
    }
    setStyle(comboStyle);
  };

  const combineLinksHandler = ({ setStyle, links }) => {
    const { color } = Object.values(links)[0];

    setStyle({
      width: Object.values(links).length ** 0.7,
      color,
    });
  };

  const changeHandler = ({ selection }) => {
    const { selection: lastSelection } = state;
    if (selection) {
      setState((current) => {
        return { ...current, selection: selection || lastSelection };
      });
    }
  };

  const doubleClickHandler = ({ id, itemType }) => {
    if (itemType === "combo") {
      setCombos((current) => {
        const { open } = current[id];
        return { ...current, [id]: { ...current[id], open: !open } };
      });

      setState((current) => {
        return { ...current, combine: { ...current.combine } };
      });
    }
  };

  const layout = () => {
    // setting a new layout object will cause a full layout
    setState((current) => {
      return { ...current, layout: { ...current.layout } };
    });
  };

  // the items with 'times' property are only the 'host' nodes
  // if a host is not inRange, then we fade it out
  // if an ip has no hosts inRange, then we fade it out
  const formatData = (inRange) => {
    const formattedItems = mapValues(items, (item, key) => {
      const { id1, id2 } = item;
      let shouldFade;
      if (id1 && id2) {
        // it's a link, so we fade it if either of its ends will be faded
        shouldFade =
          shouldFadeNode(items[id1], id1, inRange) || shouldFadeNode(items[id2], id2, inRange);
      } else {
        shouldFade = shouldFadeNode(item, key, inRange);
      }
      return { ...item, fade: shouldFade };
    });
    return formattedItems;
  };

  const { inRange, combine, selection } = state;
  const { selected, color } = timeBarSelection(selection);
  return (
    <div className="story">
      <div className="options">
        <button type="button" onClick={layout}>
          Layout
        </button>
      </div>
      <div className="chart-wrapper">
        <Chart
          style={{ width: "100%", height: "100%" }}
          items={                   }
          combine={combine}
          layout={state.layout}
          options={{
            iconFontFamily: "Font Awesome 5 Free",
            marqueeLinkSelection: "off",
            selection: { color: style.colors.charcoal },
          }}
          onChange={changeHandler}
          onCombineNodes={combineNodesHandler}
          onCombineLinks={combineLinksHandler}
          onDoubleClick={doubleClickHandler}
        />
      </div>
      <TimeBar
        items={items}
        style={{ width: "100%", height: "100px" }}
        onChange={setTimeRange}
        options={{
          backgroundColor: style.colors.offWhite,
          style: { ...style.secondary1 },
        }}
        selection={selected}
        selectionColor={color}
      />
    </div>
  );
}

const FontReadyChart = React.lazy(() =>
  document.fonts.load("900 24px 'Font Awesome 5 Free'").then(() => ({
    default: () => <ComboTimeBar items={            } />,
  }))
);

export function Demo() {
  return (
    <React.Suspense fallback="">
      <FontReadyChart />
    </React.Suspense>
  );
}

const root = createRoot(document.getElementById("regraph"));
root.render(<Demo />);
const Australia = "/public/img/flags/australia.svg";
const Brazil = "/public/img/flags/brazil.svg";
const Britain = "/public/img/flags/united-kingdom.svg";
const Bulgaria = "/public/img/flags/bulgaria.svg";
const Chile = "/public/img/flags/chile.svg";
const China = "/public/img/flags/china.svg";
const Denmark = "/public/img/flags/denmark.svg";
const Estonia = "/public/img/flags/estonia.svg";
const France = "/public/img/flags/france.svg";
const Germany = "/public/img/flags/germany.svg";
const Guatemala = "/public/img/flags/guatemala.svg";
const HongKong = "/public/img/flags/hong-kong.svg";
const India = "/public/img/flags/india.svg";
const Indonesia = "/public/img/flags/indonesia.svg";
const Italy = "/public/img/flags/italy.svg";
const Japan = "/public/img/flags/japan.svg";
const Kazakhstan = "/public/img/flags/kazakhstan.svg";
const Latvia = "/public/img/flags/latvia.svg";
const Moldova = "/public/img/flags/moldova.svg";
const Netherlands = "/public/img/flags/netherlands.svg";
const NewZealand = "/public/img/flags/new-zealand.svg";
const Poland = "/public/img/flags/republic-of-poland.svg";
const Portugal = "/public/img/flags/portugal.svg";
const RepCzech = "/public/img/flags/czech-republic.svg";
const Romania = "/public/img/flags/romania.svg";
const Russia = "/public/img/flags/russia.svg";
const Singapore = "/public/img/flags/singapore.svg";
const SouthKorea = "/public/img/flags/south-korea.svg";
const Spain = "/public/img/flags/spain.svg";
const Sweden = "/public/img/flags/sweden.svg";
const Thailand = "/public/img/flags/thailand.svg";
const Turkey = "/public/img/flags/turkey.svg";
const USA = "/public/img/flags/united-states-of-america.svg";
const Ukraine = "/public/img/flags/ukraine.svg";
const Vietnam = "/public/img/flags/vietnam.svg";

const IPAddress = "/public/img/ip-address.png";

import { style } from "@ci/theme/rg/js/storyStyles";

function data() {
  const items = {
    "host-cwprfpjtmjb.biz": {
      label: [{ text: "Host\ncwprfpjtmjb.biz", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "USA",
      },
      size: 1,
      times: [{ time: 1461956160000 }],
    },
    "69.195.129.70": {
      label: [{ text: "69.195.129.70", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "USA",
        country: "USA",
        host: [
          "host-cwprfpjtmjb.biz",
          "host-blxbymhjva.info",
          "host-sqrgvbgfyya.org",
          "host-dwytqrgblrynsgtew.org",
          "host-hmndhdbscgru.pw",
          "host-uxvvm.us",
          "host-luvenxj.uk",
        ],
      },
      size: 1,
      glyphs: [{ image: USA, position: "ne" }],
    },
    "host-83.217.8.155": {
      label: [{ text: "Host\n83.217.8.155", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Russia",
      },
      size: 1,
      times: [{ time: 1461946860000 }],
    },
    "83.217.8.155": {
      label: [{ text: "83.217.8.155", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Russia",
        country: "Russia",
        host: ["host-83.217.8.155"],
      },
      size: 1,
      glyphs: [{ image: Russia, position: "ne" }],
    },
    "host-89.108.84.155": {
      label: [{ text: "Host\n89.108.84.155", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Russia",
      },
      size: 1,
      times: [{ time: 1461929940000 }],
    },
    "89.108.84.155": {
      label: [{ text: "89.108.84.155", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Russia",
        country: "Russia",
        host: ["host-89.108.84.155"],
      },
      size: 1,
      glyphs: [{ image: Russia, position: "ne" }],
    },
    "host-gmtuae.com": {
      label: [{ text: "Host\ngmtuae.com", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "Singapore",
      },
      size: 1,
      times: [{ time: 1461900480000 }],
    },
    "182.50.158.108": {
      label: [{ text: "182.50.158.108", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Singapore",
        country: "Singapore",
        host: ["host-gmtuae.com"],
      },
      size: 1,
      glyphs: [{ image: Singapore, position: "ne" }],
    },
    "host-91.234.32.19": {
      label: [{ text: "Host\n91.234.32.19", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Ukraine",
      },
      size: 1,
      times: [{ time: 1461876240000 }],
    },
    "91.234.32.19": {
      label: [{ text: "91.234.32.19", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Ukraine",
        country: "Ukraine",
        host: ["host-91.234.32.19"],
      },
      size: 1,
      glyphs: [{ image: Ukraine, position: "ne" }],
    },
    "host-faenzabike.makkie.com": {
      label: [{ text: "Host\nfaenzabike.makkie.com", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "Italy",
      },
      size: 1,
      times: [{ time: 1461873060000 }],
    },
    "213.26.174.81": {
      label: [{ text: "213.26.174.81", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Italy",
        country: "Italy",
        host: ["host-faenzabike.makkie.com"],
      },
      size: 1,
      glyphs: [{ image: Italy, position: "ne" }],
    },
    "host-51.254.240.60": {
      label: [{ text: "Host\n51.254.240.60", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "France",
      },
      size: 1,
      times: [{ time: 1461862800000 }],
    },
    "51.254.240.60": {
      label: [{ text: "51.254.240.60", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "France",
        country: "France",
        host: ["host-51.254.240.60"],
      },
      size: 1,
      glyphs: [{ image: France, position: "ne" }],
    },
    "host-axnemuevqnstqyflb.work": {
      label: [{ text: "Host\naxnemuevqnstqyflb.work", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Rep Czech",
      },
      size: 1,
      times: [{ time: 1461858660000 }],
    },
    "31.148.99.188": {
      label: [{ text: "31.148.99.188", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Rep Czech",
        country: "Rep Czech",
        host: ["host-axnemuevqnstqyflb.work"],
      },
      size: 1,
      glyphs: [{ image: RepCzech, position: "ne" }],
    },
    "host-83.217.26.168": {
      label: [{ text: "Host\n83.217.26.168", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Russia",
      },
      size: 1,
      times: [{ time: 1461852600000 }],
    },
    "83.217.26.168": {
      label: [{ text: "83.217.26.168", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Russia",
        country: "Russia",
        host: ["host-83.217.26.168"],
      },
      size: 1,
      glyphs: [{ image: Russia, position: "ne" }],
    },
    "host-bestinghana.com": {
      label: [{ text: "Host\nbestinghana.com", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "USA",
      },
      size: 1,
      times: [{ time: 1461843480000 }],
    },
    "184.168.51.1": {
      label: [{ text: "184.168.51.1", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "USA",
        country: "USA",
        host: ["host-bestinghana.com"],
      },
      size: 1,
      glyphs: [{ image: USA, position: "ne" }],
    },
    "host-coolcases.info": {
      label: [{ text: "Host\ncoolcases.info", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "USA",
      },
      size: 1,
      times: [{ time: 1461820200000 }],
    },
    "72.167.232.144": {
      label: [{ text: "72.167.232.144", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "USA",
        country: "USA",
        host: ["host-coolcases.info"],
      },
      size: 1,
      glyphs: [{ image: USA, position: "ne" }],
    },
    "host-htankds.info": {
      label: [{ text: "Host\nhtankds.info", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Ukraine",
      },
      size: 1,
      times: [{ time: 1461816000000 }],
    },
    "91.219.31.18": {
      label: [{ text: "91.219.31.18", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Ukraine",
        country: "Ukraine",
        host: ["host-htankds.info"],
      },
      size: 1,
      glyphs: [{ image: Ukraine, position: "ne" }],
    },
    "host-107.170.20.33": {
      label: [{ text: "Host\n107.170.20.33", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "USA",
      },
      size: 1,
      times: [{ time: 1461784740000 }],
    },
    "107.170.20.33": {
      label: [{ text: "107.170.20.33", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "USA",
        country: "USA",
        host: ["host-107.170.20.33"],
      },
      size: 1,
      glyphs: [{ image: USA, position: "ne" }],
    },
    "host-runescape-autominer.info": {
      label: [
        { text: "Host\nrunescape-autominer.info", ...style.darkLabelLow },
      ],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "USA",
      },
      size: 1,
      times: [{ time: 1461772260000 }],
    },
    "192.185.46.61": {
      label: [{ text: "192.185.46.61", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "USA",
        country: "USA",
        host: ["host-runescape-autominer.info"],
      },
      size: 1,
      glyphs: [{ image: USA, position: "ne" }],
    },
    "host-www.teacherassist.info": {
      label: [{ text: "Host\nwww.teacherassist.info", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "Netherlands",
      },
      size: 1,
      times: [{ time: 1461768120000 }],
    },
    "94.124.120.61": {
      label: [{ text: "94.124.120.61", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Netherlands",
        country: "Netherlands",
        host: ["host-www.teacherassist.info"],
      },
      size: 1,
      glyphs: [{ image: Netherlands, position: "ne" }],
    },
    "host-kortingcodes.be": {
      label: [{ text: "Host\nkortingcodes.be", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "USA",
      },
      size: 1,
      times: [{ time: 1461690360000 }],
    },
    "108.167.181.253": {
      label: [{ text: "108.167.181.253", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "USA",
        country: "USA",
        host: ["host-kortingcodes.be"],
      },
      size: 1,
      glyphs: [{ image: USA, position: "ne" }],
    },
    "host-custommerchandisingservices.com": {
      label: [
        {
          text: "Host\ncustommerchandisingservices.com",
          ...style.darkLabelLow,
        },
      ],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "USA",
      },
      size: 1,
      times: [{ time: 1461647280000 }],
    },
    "45.79.161.27": {
      label: [{ text: "45.79.161.27", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "USA",
        country: "USA",
        host: ["host-custommerchandisingservices.com"],
      },
      size: 1,
      glyphs: [{ image: USA, position: "ne" }],
    },
    "host-bonjourtablier.com": {
      label: [{ text: "Host\nbonjourtablier.com", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "Germany",
      },
      size: 1,
      times: [{ time: 1461442680000 }],
    },
    "212.227.247.229": {
      label: [{ text: "212.227.247.229", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Germany",
        country: "Germany",
        host: ["host-bonjourtablier.com"],
      },
      size: 1,
      glyphs: [{ image: Germany, position: "ne" }],
    },
    "host-blackroom.club": {
      label: [{ text: "Host\nblackroom.club", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "Russia",
      },
      size: 1,
      times: [{ time: 1461370680000 }],
    },
    "81.177.135.232": {
      label: [{ text: "81.177.135.232", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Russia",
        country: "Russia",
        host: ["host-blackroom.club"],
      },
      size: 1,
      glyphs: [{ image: Russia, position: "ne" }],
    },
    "host-artsabc.com": {
      label: [{ text: "Host\nartsabc.com", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "USA",
      },
      size: 1,
      times: [{ time: 1461274500000 }],
    },
    "204.12.208.74": {
      label: [{ text: "204.12.208.74", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "USA",
        country: "USA",
        host: ["host-artsabc.com"],
      },
      size: 1,
      glyphs: [{ image: USA, position: "ne" }],
    },
    "host-blessingshealthuk.com": {
      label: [{ text: "Host\nblessingshealthuk.com", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "USA",
      },
      size: 1,
      times: [{ time: 1461274500000 }],
    },
    "107.180.50.165": {
      label: [{ text: "107.180.50.165", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "USA",
        country: "USA",
        host: ["host-blessingshealthuk.com"],
      },
      size: 1,
      glyphs: [{ image: USA, position: "ne" }],
    },
    "host-anybug.net": {
      label: [{ text: "Host\nanybug.net", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "France",
      },
      size: 1,
      times: [{ time: 1461180240000 }],
    },
    "78.217.205.113": {
      label: [{ text: "78.217.205.113", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "France",
        country: "France",
        host: ["host-anybug.net"],
      },
      size: 1,
      glyphs: [{ image: France, position: "ne" }],
    },
    "host-alushtadom.com": {
      label: [{ text: "Host\nalushtadom.com", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "Russia",
      },
      size: 1,
      times: [{ time: 1461130920000 }],
    },
    "81.177.140.186": {
      label: [{ text: "81.177.140.186", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Russia",
        country: "Russia",
        host: ["host-alushtadom.com"],
      },
      size: 1,
      glyphs: [{ image: Russia, position: "ne" }],
    },
    "host-blxbymhjva.info": {
      label: [{ text: "Host\nblxbymhjva.info", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "USA",
      },
      size: 1,
      times: [{ time: 1461076140000 }],
    },
    "host-ahsqbeospcdrngfv.info": {
      label: [{ text: "Host\nahsqbeospcdrngfv.info", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Portugal",
      },
      size: 1,
      times: [{ time: 1461036300000 }],
    },
    "195.22.28.198": {
      label: [{ text: "195.22.28.198", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Portugal",
        country: "Portugal",
        host: [
          "host-ahsqbeospcdrngfv.info",
          "host-jghbktqepe.pw",
          "host-gsebqsi.ru",
          "host-ywjgjvpuyitnbiw.info",
          "host-kypsuw.pw",
          "host-kqlxtqptsmys.in",
        ],
      },
      size: 1,
      glyphs: [{ image: Portugal, position: "ne" }],
    },
    "host-cxlgwofgrjfoaa.info": {
      label: [{ text: "Host\ncxlgwofgrjfoaa.info", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Portugal",
      },
      size: 1,
      times: [{ time: 1461036240000 }],
    },
    "195.22.28.197": {
      label: [{ text: "195.22.28.197", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Portugal",
        country: "Portugal",
        host: [
          "host-cxlgwofgrjfoaa.info",
          "host-barjhxoye.info",
          "host-glhxgchhfemcjgr.pw",
          "host-fitga.ru",
          "host-wdvxeval.ru",
          "host-bnjhx.eu",
        ],
      },
      size: 1,
      glyphs: [{ image: Portugal, position: "ne" }],
    },
    "host-91.234.35.243": {
      label: [{ text: "Host\n91.234.35.243", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Ukraine",
      },
      size: 1,
      times: [{ time: 1460970000000 }],
    },
    "91.234.35.243": {
      label: [{ text: "91.234.35.243", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Ukraine",
        country: "Ukraine",
        host: ["host-91.234.35.243"],
      },
      size: 1,
      glyphs: [{ image: Ukraine, position: "ne" }],
    },
    "host-4turka.com": {
      label: [{ text: "Host\n4turka.com", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "Turkey",
      },
      size: 1,
      times: [{ time: 1460953560000 }],
    },
    "185.12.108.138": {
      label: [{ text: "185.12.108.138", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Turkey",
        country: "Turkey",
        host: ["host-4turka.com"],
      },
      size: 1,
      glyphs: [{ image: Turkey, position: "ne" }],
    },
    "host-185.14.28.30": {
      label: [{ text: "Host\n185.14.28.30", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Netherlands",
      },
      size: 1,
      times: [{ time: 1460901960000 }],
    },
    "185.14.28.30": {
      label: [{ text: "185.14.28.30", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Netherlands",
        country: "Netherlands",
        host: ["host-185.14.28.30"],
      },
      size: 1,
      glyphs: [{ image: Netherlands, position: "ne" }],
    },
    "host-31.184.196.74": {
      label: [{ text: "Host\n31.184.196.74", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Russia",
      },
      size: 1,
      times: [{ time: 1460872140000 }],
    },
    "31.184.196.74": {
      label: [{ text: "31.184.196.74", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Russia",
        country: "Russia",
        host: ["host-31.184.196.74"],
      },
      size: 1,
      glyphs: [{ image: Russia, position: "ne" }],
    },
    "host-91.230.211.103": {
      label: [{ text: "Host\n91.230.211.103", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Russia",
      },
      size: 1,
      times: [{ time: 1460872140000 }],
    },
    "91.230.211.103": {
      label: [{ text: "91.230.211.103", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Russia",
        country: "Russia",
        host: ["host-91.230.211.103"],
      },
      size: 1,
      glyphs: [{ image: Russia, position: "ne" }],
    },
    "host-91.219.29.81": {
      label: [{ text: "Host\n91.219.29.81", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Ukraine",
      },
      size: 1,
      times: [{ time: 1460866020000 }],
    },
    "91.219.29.81": {
      label: [{ text: "91.219.29.81", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Ukraine",
        country: "Ukraine",
        host: ["host-91.219.29.81"],
      },
      size: 1,
      glyphs: [{ image: Ukraine, position: "ne" }],
    },
    "host-jghbktqepe.pw": {
      label: [{ text: "Host\njghbktqepe.pw", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Portugal",
      },
      size: 1,
      times: [{ time: 1460821320000 }],
    },
    "host-lorangeriedelareine.fr": {
      label: [{ text: "Host\nlorangeriedelareine.fr", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "TeslaCrypt",
        ransomware: "TeslaCrypt",
        from: "France",
      },
      size: 1,
      times: [{ time: 1460742780000 }],
    },
    "62.210.116.247": {
      label: [{ text: "62.210.116.247", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "France",
        country: "France",
        host: ["host-lorangeriedelareine.fr"],
      },
      size: 1,
      glyphs: [{ image: France, position: "ne" }],
    },
    "host-gfcuxnaek.ru": {
      label: [{ text: "Host\ngfcuxnaek.ru", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "Portugal",
      },
      size: 1,
      times: [{ time: 1460713140000 }],
    },
    "195.22.28.199": {
      label: [{ text: "195.22.28.199", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "Portugal",
        country: "Portugal",
        host: [
          "host-gfcuxnaek.ru",
          "host-ampjsppmftmfdblpt.info",
          "host-kcdfajaxngiff.info",
          "host-bqbbsfdw.be",
          "host-jxqdry.ru",
          "host-svkjhguk.ru",
        ],
      },
      size: 1,
      glyphs: [{ image: Portugal, position: "ne" }],
    },
    "host-uhhvhjqowpgopq.xyz": {
      label: [{ text: "Host\nuhhvhjqowpgopq.xyz", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",
        ransomware: "Locky",
        from: "USA",
      },
      size: 1,
      times: [{ time: 1460713140000 }],
    },
    "208.100.26.234": {
      label: [{ text: "208.100.26.234", ...style.darkLabelLow }],
      image: IPAddress,
      ...style.trans,
      data: {
        type: "ip",
        group: "USA",
        country: "USA",
        host: [
          "host-uhhvhjqowpgopq.xyz",
          "host-omeaswslhgdw.xyz",
          "host-swfqg.in",
          "host-plfbvdrpvsm.pw",
        ],
      },
      size: 1,
      glyphs: [{ image: USA, position: "ne" }],
    },
    "host-ampjsppmftmfdblpt.info": {
      label: [{ text: "Host\nampjsppmftmfdblpt.info", ...style.darkLabelLow }],
      ...style.trans,
      fontIcon: { text: "fas fa-server", color: "#444" },
      data: {
        type: "host",
        group: "Locky",

// ...truncated 10360 lines
<!doctype html>
<html>
  <body>
    <div id="regraph" style="height: 100vh"></div>
    <script type="module" src="./code.jsx"></script>
  </body>
</html>

Terms of use

These terms do not alter or supersede any existing agreements between you (or your employer) and us.

By accessing or using any Content you agree to be bound by these Terms of Use. Please review these terms carefully before using the website.

The contents of this website, including but not limited to any text, code samples, API references, schemas, interactive tools, and other materials (collectively, the 'Content'), are made available for informational and internal evaluation purposes only. All intellectual property rights in the Content are reserved. No licence is granted to use the Content for any commercial purpose, or to copy, distribute, modify, reverse-engineer, or incorporate any part of the Content into any product or service, without our prior written consent.

This Content is provided “as is” and “as available,” without any representations, warranties, or guarantees of any kind, whether express or implied, including but not limited to implied warranties of merchantability, fitness for a particular purpose, non-infringement, or accuracy. To the fullest extent permitted by applicable law, we expressly exclude and disclaim all implied warranties, conditions, and other terms that might otherwise be implied.

We disclaim all liability for any loss or damage, whether direct, indirect, incidental, consequential, or otherwise, arising from any reliance placed on the Content or from your use of it, to the fullest extent permitted by applicable law. By continuing to access or use the Content, you acknowledge and agree to these terms.