Skip to content

PluginStructure

Tim Erickson edited this page Sep 21, 2018 · 7 revisions

The structure of a plugin

A typical plugin has a more-or-less common structure, although (a) it evolved during the course of the original project, 2015–2018; (b) Tim often changed, arbitrarily, the naming conventions, often slightly, due to forgetting what they were; and (c) some plugins require different resources, such as connections to php and mySQL.

the main directory

A plugin named foo resides in /plugins/foo/. Most of the code sits in that file, with other resources in subfolders such as art/ or data/.

the html file

This is named foo.html

Its <head> section contains a slew of includes, often looking something like this:

    <link href='https://fonts.googleapis.com/css?family=Maven+Pro:700,900' rel='stylesheet'>
    <link href='https://fonts.googleapis.com/css?family=Rokkitt:300, 700' rel='stylesheet'>

    <link rel='stylesheet' type='text/css' href='foo.css'/>
    <link rel='stylesheet' type='text/css' href='../common/jqueryFolder/jquery-ui.min.css'/>

    <script src="../common/iframe-phone.js" language="javascript"></script>
    <script src="../common/codapInterface.js" language="javascript"></script>
    <script src="../common/pluginHelper.js" language="javascript"></script>
    <script src="../common/TEEUtils.js" language="javascript"></script>
    <script src="../common/jqueryFolder/jquery.min.js" language="javascript"></script>
    <script src="../common/jqueryFolder/jquery-ui.min.js" language="javascript"></script>

    <script src="foo.js" language="JavaScript"></script>

    <script src="fooGameConfigurations.js" language="JavaScript"></script>
    <script src="fooStrings.js" language="JavaScript"></script>

    <script src="foophpConnector.js" language="JavaScript"></script>
    <script src="fooCODAPConnector.js" language="JavaScript"></script>
    <script src="fooUI.js" language="JavaScript"></script>
    <script src="fooUserActions.js" language="JavaScript"></script>
    <script src="fooModel.js" language="JavaScript"></script>

Note that there is usually a foo.css to style the plugin.

foo.js

This is a central site for js functionality. As Tim developed a prototype, this was often the first js file he made, and then, as things got more complex, functions and members were offloaded into other files. In a more mature plugin, it functions as a controller, but often also implements functionality that has not yet found a home.

It defines the top-level global named foo; ideally it's the only one. So we often see:

let foo = {           //  top level global
    state : {},

at the top of this file. Note that this is where foo.state gets defined.

foo.constants.js

Sometimes the object foo.constants gets its own file; sometimes it lives in foo.js.

foo.CODAPConnect.js

Sometimes fooCODAPConnect.js or simply CODAPconnect.js or other variants. In ay case, defines the object foo.CODAPConnect or foo.connector.

This file has all the routines we use to communicate with CODAP, particularly emitting data we have generated into the platform. Here is an example from fishCODAPConnector.js:

    createFishItems: async function (iValues) {

        iValues = pluginHelper.arrayify(iValues);
        console.log("Fish ... createFishItems with " + iValues.length + " case(s)");

        try {
            res = await pluginHelper.createItems(iValues, fish.constants.kFishDataSetName);
            console.log("Resolving createFishItems() with " + JSON.stringify(res));
            return res;
        } catch {
            console.log("Problem creating items using iValues = " + JSON.stringify(iValues));
        }
    },

In this example, we have finally gotten wise to asynchronous coding (note the async function in the definition and the await where we ask pluginHelper to create the items). Earlier plugins do not have this; that's a good project to undertake one day.

foo.phpConnector.js

If the plugin needs a database (we've used mySQL during prototyping, but changing to something better would make sense), you will need php to communicate with it.

This file contains routines that set up the parameters for a specific task, and one method that actually makes the call. We are using the Fetch system of communicating. Here are two routines from fish.phpConnector.js for your perusal:

    sendCommand: async function (iCommands) {
        const theCommand = iCommands.c;

        let theBody = new FormData();
        for (let key in iCommands) {
            if (iCommands.hasOwnProperty(key)) {
                theBody.append(key, iCommands[key])
            }
        }
        theBody.append("whence", fish.whence);      //  here is where the JS tells the PHP which server we're on.

        let theRequest = new Request(
            fish.constants.kBaseURL[fish.whence],
            {method: 'POST', body: theBody, headers: new Headers()}
        );

        try {
            const theResult = await fetch(theRequest);   // here (finally) is the fetch!
            if (theResult.ok) {
                const theJSON = await theResult.json();
                return theJSON;
            } else {
                console.error("sendCommand error: " + theResult.statusText);
            }
        }
        catch (msg) {
            console.log('fetch sequence error: ' + msg);
        }
    },

    getGameData: async function () {
        try {
            const theCommands = {"c": "gameData", "gameCode": fish.state.gameCode};
            const iData = await fish.phpConnector.sendCommand(theCommands);
            return iData;
        }

        catch (msg) {
            console.log('get game data error: ' + msg);
        }
    },

In this example, suppose some other routine needs game data from the DB. It calls await fish.phpConnector.getGameData(). This is the bottom routine in the example. That function constructs the commands that php will need (they will be $_REQUEST variables on the inside) in the object theCommands. Then it asks sendCommand to send them.

sendCommand, for its part, does a little dance. It:

  • translates the commands object into a FormData called theBody;
  • adds an extra command, whence, which tells us what system we're on (e.g., "local");
  • creates a Request object that includes the commands as well as the URL for the php file (which depends on whence as well);
  • finally performs the fetch(), awaits its completion (it's a Promise), and returns theResult;
  • extracts the JSON version of theResult and returns that JSON.

foo.model.js

If a pure model file makes sense, this is a good place to put it.

Clone this wiki locally