Source: visual-layer/src/layers/point/renderer.js

/* global Element, document  */
import {
    makeElement,
    selectElement,
    easeFns,
    objectIterator,
    getSymbol
} from 'muze-utils';

/**
 *
 *
 * @param {*} str
 *
 */
const checkPath = (str) => {
    if (/^[mzlhvcsqta]\s*[-+.0-9][^mlhvzcsqta]+/i.test(str) && /[\dz]$/i.test(str) && str.length > 4) {
        return true;
    }
    return false;
};

/**
 *
 *
 * @param {*} d
 * @param {*} elem
 */
const createShape = function (d, elem) {
    const groupElement = selectElement(elem);
    const { shape, size, update } = d;

    if (shape instanceof Promise) {
        shape.then((res) => {
            d.shape = res;
            createShape(d, elem);
        });
    } else if (shape instanceof Element) {
        let newShape = shape.cloneNode(true);

        if (newShape.nodeName.toLowerCase() === 'img') {
            const src = newShape.src || newShape.href;
            newShape = document.createElementNS('http://www.w3.org/2000/svg', 'image');
            newShape.setAttribute('href', src);
        }
        const shapeElement = selectElement(newShape);
        if (newShape.nodeName === 'path' || newShape.nodeName === 'image') {
            shapeElement.attr('transform', `scale(${size / 100})`);
        } else {
            shapeElement.attr('height', size);
            shapeElement.attr('width', size);
        }
        shapeElement.attr('x', -size / 2);
        shapeElement.attr('y', -size / 2);
        selectElement(groupElement.node().appendChild(newShape));
    } else if (typeof shape === 'string') {
        let pathStr;
        if (checkPath(shape)) {
            pathStr = shape;
        } else {
            pathStr = getSymbol(shape).size(size)(update);
        }
        makeElement(groupElement, 'path', data => [data]).attr('d', pathStr);
    } else {
        d.shape = 'circle';
        createShape(d, elem);
    }
};

/**
 * Draws symbols using d3 symbol api
 * @param {Object} params Contains the svg container, points and other symbol related attributes.
 */
/* istanbul ignore next */ const drawSymbols = (params) => {
    let mergedGroups;
    const { container, points, transition, className } = params;
    const { duration, effect, disabled } = transition;
    const mount = selectElement(container);

    mount.attr('class', className);
    const symbolGroups = mount.selectAll('g').data(points, params.keyFn);
    const symbolEnter = symbolGroups.enter().append('g').attr('transform', d => `translate(${d.enter.x},${d.enter.y})`);
    mergedGroups = symbolGroups.merge(symbolEnter)
                    .each(function (d) {
                        createShape(d, this);
                    });
    mergedGroups = disabled ? mergedGroups : mergedGroups.transition().duration(disabled ? 0 : transition.duration);
    mergedGroups.attr('transform', d => `translate(${d.update.x},${d.update.y})`)
                    .each(function (d) {
                        const style = d.style;
                        const element = selectElement(this);
                        objectIterator(style, key => element.style(key, style[key]));
                        element.attr('class', `${className}`);
                        element.classed(d.className, true);
                    });

    const exitGroups = symbolGroups.exit();
    if (!disabled) {
        exitGroups.transition().ease(easeFns[effect])
                        .duration(duration)
                        .on('end', function () {
                            selectElement(this).remove();
                        })
                        .style('fill-opacity', 0)
                        .style('stroke-opacity', 0);
    } else {
        exitGroups.remove();
    }
};

export default drawSymbols;