text: function getY(max, height, diff, value) {
    return parseFloat((height - (value * height / max) + diff).toFixed(2));
  }
  
  function removeChildren(svg) {
    [...svg.querySelectorAll("*")].forEach(element => svg.removeChild(element));
  }
  
  function defaultFetch(entry) {
    return entry.value;
  }
  
  function buildElement(tag, attrs) {
    const element = document.createElementNS("http://www.w3.org/2000/svg", tag);
  
    for (let name in attrs) {
      element.setAttribute(name, attrs[name]);
    }
  
    return element;
  }
  
  export function sparkline(svg, entries, options) {
    removeChildren(svg);
  
    if (entries.length <= 1) {
      return;
    }
  
    options = options || {};
  
    if (typeof(entries[0]) === "number") {
      entries = entries.map(entry => {
        return {value: (isNaN(entry) ? 0 : entry)};
      });
    }
  
    // This function will be called whenever the mouse moves
    // over the SVG. You can use it to render something like a
    // tooltip.
    const onmousemove = options.onmousemove;
  
    // This function will be called whenever the mouse leaves
    // the SVG area. You can use it to hide the tooltip.
    const onmouseout = options.onmouseout;
  
    // Should we run in interactive mode? If yes, this will handle the
    // cursor and spot position when moving the mouse.
    const interactive = ("interactive" in options) ? options.interactive : !!onmousemove;
  
    // Define how big should be the spot area.
    const spotRadius = options.spotRadius || 2;
    const spotDiameter = spotRadius * 2;
  
    // Define how wide should be the cursor area.
    const cursorWidth = options.cursorWidth || 2;
  
    // Get the stroke width; this is used to compute the
    // rendering offset.
    const strokeWidth = parseFloat(svg.attributes["stroke-width"].value);
  
    // By default, data must be formatted as an array of numbers or
    // an array of objects with the value key (like `[{value: 1}]`).
    // You can set a custom function to return data for a different
    // data structure.
    const fetch = options.fetch || defaultFetch;
  
    // Retrieve only values, easing the find for the maximum value.
    const values = entries.map(entry => fetch(entry));
  
    // The rendering width will account for the spot size.
    const width = parseFloat(svg.attributes.width.value) - spotDiameter * 2;
  
    // Get the SVG element's full height.
    // This is used
    const fullHeight = parseFloat(svg.attributes.height.value);
  
    // The rendering height accounts for stroke width and spot size.
    const height = fullHeight - (strokeWidth * 2) - spotDiameter;
  
    // The maximum value. This is used to calculate the Y coord of
    // each sparkline datapoint.
    // Make sure to force it to 0 if there is no maximum value
    let max = Math.max(...values);
    max = (isNaN(max) ? 0 : max);
  
    // Some arbitrary value to remove the cursor and spot out of
    // the viewing canvas.
    const offscreen = -1000;
  
    // Cache the last item index.
    const lastItemIndex = values.length - 1;
  
    // Calculate the X coord base step.
    const offset = width / lastItemIndex;
  
    // Hold all datapoints, which is whatever we got as the entry plus
    // x/y coords and the index.
    const datapoints = [];
  
    // Hold the line coordinates.
    let pathY = getY(max, height, strokeWidth + spotRadius, values[0]);
    pathY = (isNaN(pathY) ? height : pathY);
  
    let pathCoords = `M${spotDiameter} ${pathY}`;
  
    values.forEach((value, index) => {
      const x = index * offset + spotDiameter;
      let y = getY(max, height, strokeWidth + spotRadius, value);
      y = (isNaN(y) ? height : y);
  
      datapoints.push(Object.assign({}, entries[index], {
        index: index,
        x: x,
        y: y
      }));
  
      pathCoords += ` L ${x} ${y}`;
    });
  
    const path = buildElement("path", {
      class: "sparkline--line",
      d: pathCoords,
      fill: "none"
    });
  
    let fillCoords = `${pathCoords} V ${fullHeight} L ${spotDiameter} ${fullHeight} Z`;
  
    const fill = buildElement("path", {
      class: "sparkline--fill",
      d: fillCoords,
      stroke: "none"
    });
  
    svg.appendChild(fill);
    svg.appendChild(path);
  
    if (!interactive) {
      return;
    }
  
    const cursor = buildElement("line", {
      class: "sparkline--cursor",
      x1: offscreen,
      x2: offscreen,
      y1: 0,
      y2: fullHeight,
      "stroke-width": cursorWidth
    });
  
    const spot = buildElement("circle", {
      class: "sparkline--spot",
      cx: offscreen,
      cy: offscreen,
      r: spotRadius
    });
  
    svg.appendChild(cursor);
    svg.appendChild(spot);
  
    const interactionLayer = buildElement("rect", {
      width: svg.attributes.width.value,
      height: svg.attributes.height.value,
      style: "fill: transparent; stroke: transparent",
      class: "sparkline--interaction-layer",
    });
    svg.appendChild(interactionLayer);
  
    interactionLayer.addEventListener("mouseout", event => {
      cursor.setAttribute("x1", offscreen);
      cursor.setAttribute("x2", offscreen);
  
      spot.setAttribute("cx", offscreen);
  
      if (onmouseout) {
        onmouseout(event);
      }
    });
  
    interactionLayer.addEventListener("mousemove", event => {
      const mouseX = event.offsetX;
  
      let nextDataPoint = datapoints.find(entry => {
        return entry.x >= mouseX;
      });
  
      if (!nextDataPoint) {
        nextDataPoint = datapoints[lastItemIndex];
      }
  
      let previousDataPoint = datapoints[datapoints.indexOf(nextDataPoint) - 1];
      let currentDataPoint;
      let halfway;
  
      if (previousDataPoint) {
        halfway = previousDataPoint.x + ((nextDataPoint.x - previousDataPoint.x) / 2);
        currentDataPoint = mouseX >= halfway ? nextDataPoint : previousDataPoint;
      } else {
        currentDataPoint = nextDataPoint;
      }
  
      const x = currentDataPoint.x;
      const y = currentDataPoint.y;
  
      spot.setAttribute("cx", x);
      spot.setAttribute("cy", y);
  
      cursor.setAttribute("x1", x);
      cursor.setAttribute("x2", x);
  
      if (onmousemove) {
        onmousemove(event, currentDataPoint);
      }
    });
  }
  
  export default sparkline;
  