Source: skadi-designer-api.js

/*   Skadi - A visual modelling tool for constructing directed graphs.

     Copyright (C) 2022-2024 Visual Topology Ltd

     Licensed under the MIT License
*/

var skadi = skadi || {};

skadi.api_home_url = document.currentScript.src.split("/").slice(0,-1).join("/");

skadi.DesignerApi = class extends skadi.Api {

    /**
     * Create a skadi graphical editor
     */
    constructor() {
        super();
        this.design = null;
        this.container = null;
        this.header = null;
        this.canvas = null;
        this.workspace_id = null;
        this.topology_store = null;
        this.splash_screen = null;
        this.restart_alert = null;
    }

    /**
     * Initialise skadi graphical editor
     *
     * @param {string} topology_id - the id of the topology to be edited
     * @param {string} element_id - the name of the document element into which skadi will be loaded
     * @param {Object} options - various options to customise skadi
     * @param {Object} plugins - various plugins to customise skadi
     */
    async init(topology_id, element_id, options, plugins) {
        let splash_options = options["designer_splash"];
        this.splash_screen = null;
        if (splash_options) {
            this.splash_screen = new skadi.Alert("Loading...", splash_options);
        }
        this.canvas_width = options.canvas_width || skadi.Api.DEFAULT_CANVAS_WIDTH;
        this.canvas_height = options.canvas_height || skadi.Api.DEFAULT_CANVAS_HEIGHT;

        this.workspace_id = options.workspace_id;
        this.topology_id = topology_id;

        // l10n

        let l10n_utils = await this.load_l10n(options.l10n_url || skadi.api_home_url + "/l10n");

        // collect plugins

        let engine = plugins.engine || new skadi.GraphWorkerEngine(
            options["in_page"] ? null : options["worker_path"],
            options["in_page"] ? null : options["extra_worker_paths"],
            options["read_only"] || false, "client");

        if (engine.restartable()) {
            engine.set_restart_callbacks(() => {
                this.restart_alert = new skadi.Alert(l10n_utils.localise("engine.restarting"));
            }, () => {
                setTimeout(() => {
                    this.restart_alert.close();
                    this.restart_alert = null;
                }, 1000);
            });
        }

        engine.set_pause_callbacks(() => {
            if (this.splash_screen === null && this.restart_alert === null) {
                let pause_alert = new skadi.Alert(l10n_utils.localise("engine.paused"));
                setTimeout(() => {
                    pause_alert.close();
                }, 1000);
            }
        }, () => {
            if (this.splash_screen === null && this.restart_alert === null) {
                let resumed_alert = new skadi.Alert(l10n_utils.localise("engine.resumed"));
                setTimeout(() => {
                    resumed_alert.close();
                }, 1000);
            }
        });

        this.topology_store = plugins.topology_store || new skadi.TopologyStore(this.workspace_id || "skadi_default_workspace", this);

        await this.topology_store.init(options);

        this.container = skadi.$$$(element_id);
        this.header = this.container.append("div").attr("class", "skadi_header");

        if (options.designer_title) {
            this.header.append("h4").attr("class", "skadi_header_title").text(options.designer_title);
            this.header.append("span").attr("class", "skadi_header_text").text(this.workspace_id + "/" + this.topology_id);
        }

        let right_div = this.header.append("div")
            .attr("class", "skadi_header_right")

        if (options.directory_url) {
            right_div.append("a").text(l10n_utils.localise("directory.open")).attr("href", options.directory_url).attr("class", "skadi_header_link");
        }

        right_div.append("img").attr("src", options.icon_url || skadi.icon_skadi).attr("class", "skadi_header_icon");

        this.canvas = this.container.append("div");

        let schema = await this.load_schema(options.package_urls);
        let core = new skadi.Core(this.workspace_id, topology_id, l10n_utils, schema, engine, this.topology_store, options.platform_extensions);

        this.set_core(core);

        let config_status_event_handler = this.core.add_configuration_status_event_handler((package_id, status_state, message) => {
            this.splash_screen.update_message(`${message}`)
        });

        await this.open_design();
        let node_renamings = {};

        await engine.bind(this);
        await engine.load();
        await core.init();

        let stored_topology = await this.topology_store.load_topology(topology_id);
        await this.load(stored_topology, node_renamings);

        this.topology_store.bind();

        if (this.splash_screen) {
            setTimeout(() => {
                this.splash_screen.close();
                this.splash_screen = null;
                this.core.remove_configuration_event_handler(config_status_event_handler);
            }, 1000);
        }
    }

    async load(from_obj, node_renamings, suppress_callbacks) {
        let load_ids =  await super.load(from_obj ,node_renamings, suppress_callbacks);
        load_ids.added_node_ids.forEach((node_id) => {
            let core_node = this.design.core.get_node(node_id);
            this.design.create_graph_node(core_node);
        });
        load_ids.added_link_ids.forEach((link_id) => {
            let core_link = this.design.core.get_link(link_id);
            this.design.create_graph_link(core_link);
        });
        return load_ids;
    }

    async open_design() {
        this.canvas.html("");
        this.canvas.attr("class","skadi_design_container");
        this.container.style("overflow","hidden");
        this.design = new skadi.Design(this.core, this.canvas, this.canvas_width, this.canvas_height);
    }

    get_id() {
        return this.topology_id;
    }

    get_workspace_id() {
        return this.workspace_id;
    }

    is_empty() {
        return (this.get_nodes().length === 0);
    }

    get_nodes() {
        return this.design.get_network().get_node_list();
    }

    get_package_ids_used_by_nodes() {
        return this.design.get_network().get_package_ids_used_by_nodes();
    }

    add_node(node_id, node_type_name, xc, yc, metadata, suppress_callbacks, copy_from_node_id) {
        let node_type = this.design.get_schema().get_node_type(node_type_name);
        this.design.create_node(node_id, node_type, xc, yc, metadata, suppress_callbacks, copy_from_node_id);
    }

    move_node(id, xc, yc, suppress_callbacks) {
        this.design.update_node_position(id, xc, yc, suppress_callbacks);
    }

    add_link(link_id, link_type, from_node_id, from_port_name, to_node_id, to_port_name, suppress_callbacks) {
        let from_node = this.design.get_node(from_node_id);
        let from_port = from_node.get_ports()[from_port_name];
        let to_node = this.design.get_node(to_node_id);
        let to_port = to_node.get_ports()[to_port_name];
        this.design.create_link(from_port, to_port, link_type, link_id, suppress_callbacks);
    }

    get_packages() {
        return this.design.get_schema().get_package_types();
    }

    remove_node(id, suppress_callbacks) {
        this.design.remove_node(id, suppress_callbacks);
    }

    remove_link(id, suppress_callbacks) {
        this.design.remove_link(id, suppress_callbacks);
    }

    clear(suppress_callbacks) {
        this.design.clear(suppress_callbacks);
    }

    close() {

    }
}


 /**
  * Asynchronous function to create a skadi graphical editor widget
  *
  * @param {string} topology_id - the id of the topology to be edited
  * @param {string} element_id - the name of the document element into which skadi will be loaded
  * @param {Object} options - various options to customise skadi
  * @param {Object} plugins - various plugins to customise skadi
  */
skadi.start_designer = async function(topology_id, element_id, options, plugins) {
    skadi.$(element_id).setAttribute("class","skadi_design_container");

    let designer_api = new skadi.DesignerApi();

    await designer_api.init(topology_id, element_id, options, plugins || {});

    return designer_api;
}