{"size": 3111, "ext": "js", "max_stars_repo_path": "src/routing/modulo-hash.js", "max_stars_repo_name": "endeepak/simufast", "max_stars_repo_stars_event_min_datetime": "2021-09-28T10:08:11.000Z", "max_stars_repo_stars_event_max_datetime": "2021-09-28T10:08:11.000Z", "max_issues_repo_path": "src/routing/modulo-hash.js", "max_issues_repo_name": "endeepak/simufast", "max_issues_repo_issues_event_min_datetime": null, "max_issues_repo_issues_event_max_datetime": null, "max_forks_repo_path": "src/routing/modulo-hash.js", "max_forks_repo_name": "endeepak/simufast", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": 1, "max_issues_count": null, "max_forks_count": null, "avg_line_length": 35.7586206897, "max_line_length": 115, "alphanum_fraction": 0.5911282546}
const { Container, Text } = require('@createjs/easeljs');
const { Tween, Ease } = require('@createjs/tweenjs');
const { HashNode } = require('./hash-node');
const { tweenPromise, getHashCode, MD5 } = require('../utils');

export class ModuloHash {
    constructor(player, options) {
        this.stage = player.getStage();
        this.options = options || {};
        this.log = player.log.bind(player);
        this.speedFn = player.getSpeed.bind(player);
        this._initVisual();
    }

    _initVisual() {
        this.nodes = [];
        this.container = new Container();
        const maxNodes = this.options.maxNodes || 3;
        const nodeRadius = this.stage.canvas.width / (2 * 10);
        const margin = nodeRadius;
        this.stage.canvas.height = (maxNodes * nodeRadius * 2) + (margin * 2);
        this.visualConfig = {
            x: this.stage.canvas.width - (3 * margin),
            y: margin,
            nodeRadius: nodeRadius
        }
        this.stage.addChild(this.container);
    }

    async addNode(nodeName, options) {
        this.log(`Adding node: ${nodeName}`);
        const point = this._getPontForNodeIndex(this.nodes.length);
        const node = new HashNode(nodeName, undefined, {
            x: point.x,
            y: point.y,
            radius: this.visualConfig.nodeRadius,
            speedFn: this.speedFn
        });
        this.nodes.push(node);
        await node.draw(this.container, options);
    }

    _getPontForNodeIndex(nodeIndex) {
        return {
            x: this.visualConfig.x - this.visualConfig.nodeRadius,
            y: this.visualConfig.y + (2 * nodeIndex) * this.visualConfig.nodeRadius + this.visualConfig.nodeRadius,
        }
    }

    async removeNode(nodeName) {
        this.log(`Removing node: ${nodeName}`);
        const node = this.nodes.filter(node => node.node === nodeName)[0];
        const nodeIndex = this.nodes.indexOf(node);
        this.nodes.splice(nodeIndex, 1);
        const promises = [node.undraw(this.container)];
        for (let i = nodeIndex; i < this.nodes.length; i++) {
            promises.push(this.nodes[i].moveTo(this._getPontForNodeIndex(i)));
        }
        await Promise.all(promises);
    }

    async getNodeForKey(key) {
        this.log(`Routing key: ${key}`);
        const nodeIndex = getHashCode(MD5(key)) % this.nodes.length;
        const node = this.nodes[nodeIndex];
        await this._visualiseNodeForKey(key, nodeIndex, node);
        return node.node;
    }

    async _visualiseNodeForKey(key, nodeIndex, node) {
        const { nodeRadius } = this.visualConfig;
        const textPoint = this._getPontForNodeIndex(nodeIndex);
        const text = new Text(key, `${nodeRadius}px Arial`);
        this.container.addChild(text);
        await Promise.all([
            tweenPromise(Tween.get(text)
                .to({ x: textPoint.x, y: textPoint.y }, 1000 / this.speedFn(), Ease.linear)),
            node.highlight()
        ])
        this.container.removeChild(text);
    }

    reset() {
        this.stage.removeChild(this.container);
        this._initVisual();
    }
}