/* 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;
}