Source: system/designer_system.js

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

     Copyright (C) 2022-2026 Visual Topology Ltd

     Licensed under the MIT License
*/

var hyrrokkin = hyrrokkin || {};

hyrrokkin.DesignerSystem = class extends hyrrokkin.SystemBase {

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

    /**
     * Initialise hyrrokkin graphical editor
     *
     * @param {string} element_id - the name of the document element into which hyrrokkin will be loaded
     * @param {Object} options - various options to customise hyrrokkin
     * @param {Object} model - class instance implementing the hyrrokkin.ModelBase API
     */
    async init(element_id, options, model) {
        this.min_window_width = options["min_window_width"] || 800;
        this.min_window_height = options["min_window_height"] || 600;
        let splash_options = options["designer_splash"];
        this.splash_screen = null;
        if (splash_options) {
            this.splash_screen = new hyrrokkin.Alert("Loading...", splash_options);
        }
        this.canvas_width = options.canvas_width || hyrrokkin.SystemBase.DEFAULT_CANVAS_WIDTH;
        this.canvas_height = options.canvas_height || hyrrokkin.SystemBase.DEFAULT_CANVAS_HEIGHT;

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

        // l10n

        let l10n_utils = await this.load_l10n();

        this.model = model;

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

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

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

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

        right_div.append("img").attr("src", options.icon_url || "hyrrokkin-ui/images/hyrrokkin-ui.svg").attr("class", "hyrrokkin_header_icon");

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

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

        this.set_core(core);

        let config_status_event_handler = this.core.add_configuration_status_event_handler((package_id, details) => {
            this.splash_screen.update_message(schema.get_package_type(package_id).localise(details["status_message"]));
        });

        await this.open_design();

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

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

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

    get_id() {
        return this.topology_id;
    }

    get_workspace_id() {
        return this.workspace_id;
    }

    add_window_size_alerts() {
        if (this.min_window_width && this.min_window_height) {
            this.design.add_window_size_alerts(this.min_window_width, this.min_window_height);
        }
    }

    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();
    }

    node_added(node_id, node_type_name, metadata, copy_from_node_id) {
        let node_type = this.design.get_schema().get_node_type(node_type_name);
        this.design.node_added(node_id, node_type, metadata, copy_from_node_id);
    }

    node_removed(node_id) {
        this.design.node_removed(node_id);
    }

    node_metadata_updated(node_id, metadata) {
        this.design.node_metadata_updated(node_id, metadata);
    }

    get_upstream_nodes(node_id) {
        return this.design.get_network().get_upstream_nodes(node_id);
    }

    get_downstream_nodes(node_id) {
        return this.design.get_network().get_downstream_nodes(node_id);
    }

    design_metadata_updated(metadata) {
        this.design.design_metadata_updated(metadata);
    }

    link_added(link_id, link_type, from_node_id, from_port_name, to_node_id, to_port_name) {
        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.link_added(from_port, to_port, link_type, link_id);
    }

    link_removed(link_id) {
        this.design.link_removed(link_id);
    }

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

    remove_node(node_id) {
        this.design.node_removed(node_id);
    }

    remove_link(link_id) {
        this.design.link_removed(link_id);
    }

    node_blocked(node_id, is_blocked) {
        this.design.node_blocked(node_id, is_blocked);
    }

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

    close() {
    }

    note_paused() {
        super.note_paused();
        this.design.note_paused();
    }

    note_resumed() {
        super.note_resumed();
        this.design.note_resumed();
    }

    note_restarting() {
        this.design.close_all_node_windows();
    }

    note_restarted() {
        this.design.note_restarted();
    }
}