Skip to content

[refactor] Improve code structure #347 #348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 133 additions & 126 deletions src/js/netjsongraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,147 +7,154 @@ const colorTool = require("zrender/lib/tool/color");
const {each} = require("zrender/lib/core/util");
const env = require("zrender/lib/core/env");

/**
* @class
* Class NetJSONGraph is entry point for NetJSONGraph library.
* Used as a global object in the examples_templates.
*/
class NetJSONGraph {
/**
* @constructor
*
* @param {string} JSONParam The NetJSON file param
* @param {Object} config
* @param {string} JSONParam - The NetJSON file parameter.
* @param {Object} [config={}] - Configuration options for the graph.
*/
constructor(JSONParam, config = {}) {
this.graph = new NetJSONGraphCore(JSONParam);
this.config = this.initializeConfig(config);
this.graph.setConfig(this.config);
this.setupGraph();
this.config.onInit.call(this.graph);
this.initializeECharts();
// eslint-disable-next-line no-constructor-return
return this.graph;
}

constructor(JSONParam, config) {
if (config && config.render === "map") {
config.render = NetJSONGraphRender.prototype.mapRender;
} else if (!config || !config.render || config.render === "graph") {
config = config || {};
config.render = NetJSONGraphRender.prototype.graphRender;
}

const graph = new NetJSONGraphCore(JSONParam);
/**
* @param {Object} config - The user-defined configuration.
* @returns {Object} - The final configuration object.
*/
initializeConfig(config = {}) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not 100% sure if in Javascript this is an antipattern how in python is, but we have been bitten quite a few times by this issue in Python: https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments

Are you sure this is not goint to be problematic here? Are you sure this change is really necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went through this doc
Evaluated at call time
The default argument is evaluated at call time. Unlike with Python (for example), a new object is created each time the function is called.
With this, I don't think there would be any problem.

return {
...config,
render:
config.render === "map"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dee077 & @nemesifier
I found this in the readme

In extreme cases, you can also pass your own render function if you don't want Echarts to render. We will pass in the processed netjson data and netjsongraph object.

https://github.com/openwisp/netjsongraph.js?tab=readme-ov-file#configuration-instructions

I think, config can be undefined and user can pass custom render function so this need to be handled.

? NetJSONGraphRender.prototype.mapRender
: NetJSONGraphRender.prototype.graphRender,
onInit: this.onInit,
onRender: this.onRender,
onUpdate: this.onUpdate,
afterUpdate: this.afterUpdate,
onLoad: this.onLoad,
};
}

Object.setPrototypeOf(NetJSONGraphRender.prototype, graph.utils);
graph.gui = new NetJSONGraphGUI(graph);
graph.utils = new NetJSONGraphRender();
graph.setUtils();
/**
* Sets up rendering utilities, GUI, and event handling. Used in constructor
*/
setupGraph() {
Object.setPrototypeOf(NetJSONGraphRender.prototype, this.graph.utils);
this.graph.gui = new NetJSONGraphGUI(this.graph);
this.graph.utils = new NetJSONGraphRender();
this.graph.setUtils();
this.graph.event = this.graph.utils.createEvent();
}

graph.event = graph.utils.createEvent();
/**
* Initializes the ECharts rendering engine. Used in constructor
*/
initializeECharts() {
this.graph.echarts = echarts.init(this.graph.el, null, {
renderer: this.graph.config.svgRender ? "svg" : "canvas",
});
}

graph.setConfig({
/**
* @function
* @name onInit
* Callback function executed on initialization
*
* @this {object} The instantiated object of NetJSONGraph
*
* @return {object} this.config
*/
onInit() {
return this.config;
},
/**
* @this {NetJSONGraph}
* @returns {Object} - The graph configuration.
*/
onInit() {
return this.config;
}

/**
* @function
* @name onRender
* Callback function executed on render start
*
* @this {object} The instantiated object of NetJSONGraph
*
* @return {object} this.config
*/
onRender() {
this.utils.showLoading.call(this);
this.gui.init();
return this.config;
},
/**
* @this {NetJSONGraph}
* @returns {Object} - The graph configuration.
*/
onRender() {
this.utils.showLoading.call(this);
this.gui.init();
return this.config;
}

/**
* @function
* @name onUpdate
* Callback function executed when data update.
*
* @this {object} The instantiated object of NetJSONGraph
*
* @return {object} this.config
*/
onUpdate() {
return this.config;
},
/**
* @this {NetJSONGraph}
* @returns {Object} - The graph configuration.
*/
onUpdate() {
return this.config;
}

/**
* @function
* @name afterUpdate
* Callback function executed after data update.
*
* @this {object} The instantiated object of NetJSONGraph
*
* @return {object} this.config
*/
afterUpdate() {
return this.config;
},
/**
* @this {NetJSONGraph}
* @returns {Object} - The graph configuration.
*/
afterUpdate() {
return this.config;
}

/**
* @function
* @name onLoad
* Callback function executed when first rendered.
*
* @this {object} The instantiated object of NetJSONGraph
*
* @return {object} this.config
*/
onLoad() {
if (this.config.metadata && this.type === "netjson") {
this.gui.createMetaInfoContainer(graph);
this.utils.updateMetadata.call(this);
/**
* In map mode, two canvas elements are used:
* - One canvas displays the map tiles (background).
* - The other canvas renders the nodes and links on top of the map.
*
* When switching to graph mode, the map canvas remains in the DOM,
* but its container's background color is updated to match the graph rendering,
*
* @this {NetJSONGraph}
* @returns {Object} - The graph configuration.
*/
onLoad() {
if (this.config.metadata && this.type === "netjson") {
this.gui.createMetaInfoContainer(this.graph);
this.utils.updateMetadata.call(this);
} else {
this.gui.nodeLinkInfoContainer = this.gui.createNodeLinkInfoContainer();
}
if (this.config.switchMode && this.type === "netjson") {
this.gui.renderModeSelector.onclick = () => {
// Switch from map to graph mode, first clear canvasContainer and then render
if (this.config.render === this.utils.mapRender) {
this.config.render = this.utils.graphRender;
const canvasContainer = this.echarts
.getZr()
.painter.getViewportRoot().parentNode;
this.echarts.clear();
this.utils.graphRender(this.data, this);
canvasContainer.style.background =
// eslint-disable-next-line no-underscore-dangle
this.echarts.getZr()._backgroundColor;

// Hide Openstreetmap credits in the bottom right corner
document.querySelector(".leaflet-control-attribution").style.display =
"none";
// Hide zoom control buttons in top right corner
document.querySelector(".leaflet-control-zoom").style.display =
"none";
} else {
this.gui.nodeLinkInfoContainer =
this.gui.createNodeLinkInfoContainer();
this.echarts.clear();
this.config.render = this.utils.mapRender;
this.utils.mapRender(this.data, this);
// Show OpenStreetMap credits and zoom control buttons in map mode
document.querySelector(".leaflet-control-attribution").style.display =
"block";
document.querySelector(".leaflet-control-zoom").style.display =
"block";
}

if (this.config.switchMode && this.type === "netjson") {
this.gui.renderModeSelector.onclick = () => {
if (this.config.render === this.utils.mapRender) {
this.config.render = this.utils.graphRender;
const canvasContainer = this.echarts
.getZr()
.painter.getViewportRoot().parentNode;
this.echarts.clear();
this.utils.graphRender(this.data, this);
canvasContainer.style.background =
// eslint-disable-next-line no-underscore-dangle
this.echarts.getZr()._backgroundColor;
document.querySelector(
".leaflet-control-attribution",
).style.display = "none";
document.querySelector(".leaflet-control-zoom").style.display =
"none";
} else {
this.echarts.clear();
this.config.render = this.utils.mapRender;
this.utils.mapRender(this.data, this);
document.querySelector(
".leaflet-control-attribution",
).style.display = "block";
document.querySelector(".leaflet-control-zoom").style.display =
"block";
}
};
}

this.utils.hideLoading.call(this);
return this.config;
},
...config,
});
graph.echarts = echarts.init(graph.el, null, {
renderer: graph.config.svgRender ? "svg" : "canvas",
});

graph.config.onInit.call(graph);

// eslint-disable-next-line no-constructor-return
return graph;
};
}
this.utils.hideLoading.call(this);
return this.config;
}
}

Expand Down
Loading