esterpantaleo / etymology

This repository contains code behind the visualization of the Wikimedia tool etytree at http://tools.wmflabs.org/etytree/
https://esterpantaleo.github.io/etymology/
50 stars 9 forks source link

Wrap visual module #28

Closed xferguson closed 7 years ago

xferguson commented 7 years ago

This is a big set of changes, and it requires a little explanation. But there are a lot of advantages to what we have changed here, including the ease of swapping out different visualization systems like dagre -> visjs, etc. This all applies to issues #24 and #15

Changes!

1. Module Changes

The files dagre.js, load.js, and sparql.js are all wrapped in a special function that "exports" them as modules:

var DB = (function(module) {
    ....
    return module;
})(DB || {});

The module names are DB for sparql.js, GRAPH for dagre.js, and LOAD for load.js. These are so we could switch out sparql or dagre if we needed to without having to change the names of any function calls in the other files.

Another positive benefit to this is that it allows us to load the modules asynchronously, and now the page loads twice as fast as it did before (initial page load, not data load from a query)

2. Bindings

Inside those module wrappers, there is a "binding" wrapper around all the rest of the functions of that file. Here's the example from the LOAD module:

var LOAD = (function(module) {

    module.bindModule = function(base, moduleName) {
        var etyBase = base;

        ...

        this.HELP = HELP;
        this.MESSAGE = MESSAGE;
        this.classes = {
            GraphNode: GraphNode,
            Node: Node
        };
        this.init = init;

        etyBase[moduleName] = this;
    };

    return module;

})(LOAD || {});

This allows us to set the EtyTree object as the base for each module so that all other modules can always be called by calling etyBase.MODULE.FUNCTION() such as etyBase.DB.lemmaQuery(). This is particularly helpful because now that means that any change we make to the data from within the modules should still stay linked properly. It also helps ensure that things stay separated so we can always keep the modules working in the future.

3. EtyTree Command Center

The etytree.js file now contains much less code. The functions that start up the tree have been moved to other files as init() functions, but they get called from etytree.js. Now etytree.js is the command center -- code that goes there is mainly to get basic settings in place and to coordinate between modules. Everything else can happen elsewhere.

EtyTree is now an object with a create() and an init() function. I'm sure that this could have been done with ES6 classes, but it was faster for me to do it this way for now. It can be changed to a class later.

The create() function binds the modules to the EtyTree object and adds config info. This is now where the ENDPOINT and debug info is stored.

The init() function runs the init() functions of any modules that have one. It also adds the etyBase.tree, which is where some data can be stored in the future. This is key because it now allows us to story information as a property of the EtyTree instead of passing it around the functions. This is where things like "inner", "g", "LangMap", and data from query responses should be stored so that they can be accessed by functions in all modules. THIS IS THE KEY TO WHY WE ARE USING MODULES -- as of now, only the LangMap gets assigned as a property of etyBase.tree, but we can start abstracting other things out of that.

The variable var ety is created as an instance of EtyTree and initialized after document load, making sure that all dependencies are loaded before anything runs.

Working With Modules

Now that we have this module system, it is critical that we continue to use the same format. It takes a little getting used to, but it will keep the etytree from falling apart.

It is very simple:

That's it!

Please take a look at these changes and make sure it all makes sense. Let me know if you have any questions.

Follow Up?

After this is merged, this opens us up to:

  1. More easily switch out dagre for visjs
  2. Add remove some of the "global" information from function arguments and set them as EtyTree properties
  3. Test different functions by only switching the reference that is assigned to the module and not having to rewrite the function call in multiple places in multiple files. ( this.render = render1 => this.render = render2, but everywhere else you would just call etyBase.GRAPH.render() ).