Source: main.js

module.exports = function(pinOriginalColor, width = 50, height = 82) {
  return markerOutliner(pinOriginalColor, width, height)
}

/**
 * Outlines Leaflet map marker pins via HTML5 Canvas.
 * @param {string} pinOriginalColor - color of the pin.
 * @param {number} width - width of the pin.
 * @param {number} height - height of the pin.
 * @return {string} - data URI in base64 representing an image.
 */
function markerOutliner(pinOriginalColor, width = 50, height = 82) {
  let { pinColor, pinColorDark } = colorExtractor(pinOriginalColor)
  let pin = pinMaker(pinColor, pinColorDark, width, height)
  return pin.toDataURL('image/png')
}

/**
 * Extracts color in RGB format.
 * @param {string} pinOriginalColor - color of the pin.
 * @return {object} - colors of pin and pin stroke.
 */
function colorExtractor(pinOriginalColor) {
  let colorDiv = document.createElement('div')
  colorDiv.style.color = pinOriginalColor
  document.body.appendChild(colorDiv)
  let pinColor = (window.getComputedStyle(colorDiv).color)
  document.body.removeChild(colorDiv)
  let pinColorDark = colorDarkening(pinColor)
  return { pinColor, pinColorDark }
}

/**
 * Makes original color darker (for stroke).
 * @param {string} pinColor - color of the pin.
 * @return {string} - color in RGB format.
 */
function colorDarkening(pinColor) {
  let rgbColorsArray = (pinColor.substring(4, pinColor.length - 1))
                         .split(',')
                         .map((item) => parseInt(item, 10))
  let darkDegree = 60
  let [red, green, blue] =
    rgbColorsArray.map((color) => color >= darkDegree ? color - darkDegree : color)
  return 'rgb(' + red + ', ' + green + ', ' + blue + ')'
}

/**
 * Outlines pin.
 * @param {string} pinColor - color of the pin.
 * @param {string} pinColorDark - color of the stroke.
 * @param {number} width - width of the pin.
 * @param {number} height - height of the pin.
 * @return {HTMLElement} - canvas.
 */
function pinMaker(pinColor, pinColorDark, width, height) {
  let element = document.createElement('canvas')
  element.setAttribute('id', 'pin')
  element.setAttribute('width', '' + width)
  element.setAttribute('height', '' + height)
  let pin = {
    x: width / 2,
    y: height,
    pinColor: pinColor,
    arcColor: 'white',
    strokeColor: pinColorDark,
  }

  let { pls, prs, arc } = setCoordinates(width, height)

  let ctx = element.getContext('2d')
  ctx.save()
  ctx.translate(pin.x, pin.y)
  ctx.beginPath()
  ctx.moveTo(0, 0)
  ctx.bezierCurveTo(pls.cp1x, pls.cp1y, pls.cp2x, pls.cp2y, pls.x, pls.y)
  ctx.bezierCurveTo(prs.cp1x, prs.cp1y, prs.cp2x, prs.cp2y, prs.x, prs.y)
  ctx.fillStyle = pin.pinColor
  ctx.fill()
  ctx.strokeStyle = pin.strokeColor
  ctx.lineWidth = 2
  ctx.stroke()
  ctx.beginPath()
  ctx.arc(arc.x, arc.y, arc.r, arc.sAngle, arc.eAngle)
  ctx.closePath()
  ctx.fillStyle = pin.arcColor
  ctx.fill()
  ctx.strokeStyle = pin.strokeColor
  ctx.lineWidth = 2
  ctx.stroke()
  ctx.restore()

  return element
}

/**
 * Sets pin coordinates for canvas outlining.
 * @param {number} width - width of the pin.
 * @param {number} height - height of the pin.
 * @return {object} - coordinates for pin outlining.
 */
function setCoordinates(width, height) {
  // Pin left side coordinates
  let pls = {}
  pls.cp1x = 0
  pls.cp1y = -5
  pls.cp2x = -width
  pls.cp2y = -(height - 2)
  pls.x = 0
  pls.y = -(height - 2)

  // Pin right side coordinates
  let prs = {}
  prs.cp1x = width
  prs.cp1y = -(height - 2)
  prs.cp2x = 0
  prs.cp2y = -5
  prs.x = 0
  prs.y = 0

  // Arc coordinates
  let arc = {}
  arc.x = 0
  arc.y = -(0.67 * height)
  arc.r = height / 9
  arc.sAngle = 0
  arc.eAngle = Math.PI * 2

  return { pls, prs, arc }
}