mouseDownEvent = (evt) => {
  const dataStorage = svgCanvas.getDataStorage()
  const selectedElements = svgCanvas.getSelectedElements()
  const zoom = svgCanvas.getZoom()
  const curShape = svgCanvas.getStyle()
  const svgRoot = svgCanvas.getSvgRoot()
  const { $id } = svgCanvas

  if (svgCanvas.spaceKey || evt.button === 1) { return }

  const rightClick = (evt.button === 2)

  if (evt.altKey) { // duplicate when dragging
    svgCanvas.cloneSelectedElements(0, 0)
  }

  svgCanvas.setRootSctm($id('svgcontent').querySelector('g').getScreenCTM().inverse())

  const pt = transformPoint(evt.clientX, evt.clientY, svgCanvas.getrootSctm())
  const mouseX = pt.x * zoom
  const mouseY = pt.y * zoom

  evt.preventDefault()

  if (rightClick) {
    if (svgCanvas.getCurrentMode() === 'path') {
      return
    }
    svgCanvas.setCurrentMode('select')
    svgCanvas.setLastClickPoint(pt)
  }

  let x = mouseX / zoom
  let y = mouseY / zoom
  let mouseTarget = svgCanvas.getMouseTarget(evt)

  if (mouseTarget.tagName === 'a' && mouseTarget.childNodes.length === 1) {
    mouseTarget = mouseTarget.firstChild
  }

  // realX/y ignores grid-snap value
  const realX = x
  svgCanvas.setStartX(x)
  svgCanvas.setRStartX(x)
  const realY = y
  svgCanvas.setStartY(y)
  svgCanvas.setRStartY(y)

  if (svgCanvas.getCurConfig().gridSnapping) {
    x = snapToGrid(x)
    y = snapToGrid(y)
    svgCanvas.setStartX(snapToGrid(svgCanvas.getStartX()))
    svgCanvas.setStartY(snapToGrid(svgCanvas.getStartY()))
  }

  // if it is a selector grip, then it must be a single element selected,
  // set the mouseTarget to that and update the mode to rotate/resize

  if (mouseTarget === svgCanvas.selectorManager.selectorParentGroup && selectedElements[0]) {
    const grip = evt.target
    const griptype = dataStorage.get(grip, 'type')
    // rotating
    if (griptype === 'rotate') {
      svgCanvas.setCurrentMode('rotate')
      // svgCanvas.setCurrentRotateMode(dataStorage.get(grip, 'dir'));
      // resizing
    } else if (griptype === 'resize') {
      svgCanvas.setCurrentMode('resize')
      svgCanvas.setCurrentResizeMode(dataStorage.get(grip, 'dir'))
    }
    mouseTarget = selectedElements[0]
  }

  svgCanvas.setStartTransform(mouseTarget.getAttribute('transform'))

  const tlist = getTransformList(mouseTarget)
  // consolidate transforms using standard SVG but keep the transformation used for the move/scale
  if (tlist.numberOfItems > 1) {
    const firstTransform = tlist.getItem(0)
    tlist.removeItem(0)
    tlist.consolidate()
    tlist.insertItemBefore(firstTransform, 0)
  }
  switch (svgCanvas.getCurrentMode()) {
    case 'select':
      svgCanvas.setStarted(true)
      svgCanvas.setCurrentResizeMode('none')
      if (rightClick) { svgCanvas.setStarted(false) }

      if (mouseTarget !== svgRoot) {
        // if this element is not yet selected, clear selection and select it
        if (!selectedElements.includes(mouseTarget)) {
          // only clear selection if shift is not pressed (otherwise, add
          // element to selection)
          if (!evt.shiftKey) {
            // No need to do the call here as it will be done on addToSelection
            svgCanvas.clearSelection(true)
          }
          svgCanvas.addToSelection([mouseTarget])
          svgCanvas.setJustSelected(mouseTarget)
          svgCanvas.pathActions.clear()
        }
        // else if it's a path, go into pathedit mode in mouseup

        if (!rightClick) {
          // insert a dummy transform so if the element(s) are moved it will have
          // a transform to use for its translate
          for (const selectedElement of selectedElements) {
            if (!selectedElement) { continue }
            const slist = getTransformList(selectedElement)
            if (slist.numberOfItems) {
              slist.insertItemBefore(svgRoot.createSVGTransform(), 0)
            } else {
              slist.appendItem(svgRoot.createSVGTransform())
            }
          }
        }
      } else if (!rightClick) {
        svgCanvas.clearSelection()
        svgCanvas.setCurrentMode('multiselect')
        if (!svgCanvas.getRubberBox()) {
          svgCanvas.setRubberBox(svgCanvas.selectorManager.getRubberBandBox())
        }
        svgCanvas.setRStartX(svgCanvas.getRStartX() * zoom)
        svgCanvas.setRStartY(svgCanvas.getRStartY() * zoom)

        assignAttributes(svgCanvas.getRubberBox(), {
          x: svgCanvas.getRStartX(),
          y: svgCanvas.getRStartY(),
          width: 0,
          height: 0,
          display: 'inline'
        }, 100)
      }
      break
    case 'zoom':
      svgCanvas.setStarted(true)
      if (!svgCanvas.getRubberBox()) {
        svgCanvas.setRubberBox(svgCanvas.selectorManager.getRubberBandBox())
      }
      assignAttributes(svgCanvas.getRubberBox(), {
        x: realX * zoom,
        y: realX * zoom,
        width: 0,
        height: 0,
        display: 'inline'
      }, 100)
      break
    case 'resize': {
      svgCanvas.setStarted(true)
      svgCanvas.setStartX(x)
      svgCanvas.setStartY(y)

      // Getting the BBox from the selection box, since we know we
      // want to orient around it
      svgCanvas.setInitBbox(getBBox($id('selectedBox0')))
      const bb = {}
      for (const [key, val] of Object.entries(svgCanvas.getInitBbox())) {
        bb[key] = val / zoom
      }
      svgCanvas.setInitBbox(bb)

      // append three dummy transforms to the tlist so that
      // we can translate,scale,translate in mousemove
      const pos = getRotationAngle(mouseTarget) ? 1 : 0

      if (hasMatrixTransform(tlist)) {
        tlist.insertItemBefore(svgRoot.createSVGTransform(), pos)
        tlist.insertItemBefore(svgRoot.createSVGTransform(), pos)
        tlist.insertItemBefore(svgRoot.createSVGTransform(), pos)
      } else {
        tlist.appendItem(svgRoot.createSVGTransform())
        tlist.appendItem(svgRoot.createSVGTransform())
        tlist.appendItem(svgRoot.createSVGTransform())
      }
      break
    }
    case 'fhellipse':
    case 'fhrect':
    case 'fhpath':
      svgCanvas.setStart({ x: realX, y: realY })
      svgCanvas.setControllPoint1('x', 0)
      svgCanvas.setControllPoint1('y', 0)
      svgCanvas.setControllPoint2('x', 0)
      svgCanvas.setControllPoint2('y', 0)
      svgCanvas.setStarted(true)
      svgCanvas.setDAttr(realX + ',' + realY + ' ')
      // Commented out as doing nothing now:
      // strokeW = parseFloat(curShape.stroke_width) === 0 ? 1 : curShape.stroke_width;
      svgCanvas.addSVGElementsFromJson({
        element: 'polyline',
        curStyles: true,
        attr: {
          points: svgCanvas.getDAttr(),
          id: svgCanvas.getNextId(),
          fill: 'none',
          opacity: curShape.opacity / 2,
          'stroke-linecap': 'round',
          style: 'pointer-events:none'
        }
      })
      svgCanvas.setFreehand('minx', realX)
      svgCanvas.setFreehand('maxx', realX)
      svgCanvas.setFreehand('miny', realY)
      svgCanvas.setFreehand('maxy', realY)
      break
    case 'image': {
      svgCanvas.setStarted(true)
      const newImage = svgCanvas.addSVGElementsFromJson({
        element: 'image',
        attr: {
          x,
          y,
          width: 0,
          height: 0,
          id: svgCanvas.getNextId(),
          opacity: curShape.opacity / 2,
          style: 'pointer-events:inherit'
        }
      })
      setHref(newImage, svgCanvas.getLastGoodImgUrl())
      preventClickDefault(newImage)
      break
    } case 'square':
    // TODO: once we create the rect, we lose information that this was a square
    // (for resizing purposes this could be important)
    // Fallthrough
    case 'rect':
      svgCanvas.setStarted(true)
      svgCanvas.setStartX(x)
      svgCanvas.setStartY(y)
      svgCanvas.addSVGElementsFromJson({
        element: 'rect',
        curStyles: true,
        attr: {
          x,
          y,
          width: 0,
          height: 0,
          id: svgCanvas.getNextId(),
          opacity: curShape.opacity / 2
        }
      })
      break
    case 'line': {
      svgCanvas.setStarted(true)
      const strokeW = Number(curShape.stroke_width) === 0 ? 1 : curShape.stroke_width
      svgCanvas.addSVGElementsFromJson({
        element: 'line',
        curStyles: true,
        attr: {
          x1: x,
          y1: y,
          x2: x,
          y2: y,
          id: svgCanvas.getNextId(),
          stroke: curShape.stroke,
          'stroke-width': strokeW,
          'stroke-dasharray': curShape.stroke_dasharray,
          'stroke-linejoin': curShape.stroke_linejoin,
          'stroke-linecap': curShape.stroke_linecap,
          'stroke-opacity': curShape.stroke_opacity,
          fill: 'none',
          opacity: curShape.opacity / 2,
          style: 'pointer-events:none'
        }
      })
      break
    } case 'circle':
      svgCanvas.setStarted(true)
      svgCanvas.addSVGElementsFromJson({
        element: 'circle',
        curStyles: true,
        attr: {
          cx: x,
          cy: y,
          r: 0,
          id: svgCanvas.getNextId(),
          opacity: curShape.opacity / 2
        }
      })
      break
    case 'ellipse':
      svgCanvas.setStarted(true)
      svgCanvas.addSVGElementsFromJson({
        element: 'ellipse',
        curStyles: true,
        attr: {
          cx: x,
          cy: y,
          rx: 0,
          ry: 0,
          id: svgCanvas.getNextId(),
          opacity: curShape.opacity / 2
        }
      })
      break
    case 'text':
      svgCanvas.setStarted(true)
      /* const newText = */ svgCanvas.addSVGElementsFromJson({
        element: 'text',
        curStyles: true,
        attr: {
          x,
          y,
          id: svgCanvas.getNextId(),
          fill: svgCanvas.getCurText('fill'),
          'stroke-width': svgCanvas.getCurText('stroke_width'),
          'font-size': svgCanvas.getCurText('font_size'),
          'font-family': svgCanvas.getCurText('font_family'),
          'text-anchor': 'middle',
          'xml:space': 'preserve',
          opacity: curShape.opacity
        }
      })
      // newText.textContent = 'text';
      break
    case 'path':
    // Fall through
    case 'pathedit':
      svgCanvas.setStartX(svgCanvas.getStartX() * zoom)
      svgCanvas.setStartY(svgCanvas.getStartY() * zoom)
      svgCanvas.pathActions.mouseDown(evt, mouseTarget, svgCanvas.getStartX(), svgCanvas.getStartY())
      svgCanvas.setStarted(true)
      break
    case 'textedit':
      svgCanvas.setStartX(svgCanvas.getStartX() * zoom)
      svgCanvas.setStartY(svgCanvas.getStartY() * zoom)
      svgCanvas.textActions.mouseDown(evt, mouseTarget, svgCanvas.getStartX(), svgCanvas.getStartY())
      svgCanvas.setStarted(true)
      break
    case 'rotate':
      svgCanvas.setStarted(true)
      // we are starting an undoable change (a drag-rotation)
      svgCanvas.undoMgr.beginUndoableChange('transform', selectedElements)
      break
    default:
      // This could occur in an extension
      break
  }

  /**
* The main (left) mouse button is held down on the canvas area.
* @event module:svgcanvas.SvgCanvas#event:ext_mouseDown
* @type {PlainObject}
* @property {MouseEvent} event The event object
* @property {Float} start_x x coordinate on canvas
* @property {Float} start_y y coordinate on canvas
* @property {Element[]} selectedElements An array of the selected Elements
*/
  const extResult = svgCanvas.runExtensions('mouseDown', {
    event: evt,
    start_x: svgCanvas.getStartX(),
    start_y: svgCanvas.getStartY(),
    selectedElements
  }, true)

  extResult.forEach((r) => {
    if (r?.started) {
      svgCanvas.setStarted(true)
    }
  })
}
Follows these conditions:
- When we are in a create mode, the element is added to the canvas but the
  action is not recorded until mousing up.
- When we are in select mode, select the element, remember the position
  and do nothing else.
@param {MouseEvent} evt
@fires module:svgcanvas.SvgCanvas#event:ext_mouseDown
@returns {void}