Skip to content
Web

Custom HTML & JavaScript

Custom Panels

Overview

AMI's frontend is broken up into "panels". Each panel has a particular type, ex: table, chart, heatmap, etc. You can implement your own panel types using the Web Panel plugin interface.

The Web Panel Plugin is a factory which generates AmiWebPluginPortlet (which represent an instance of a panel)

Java interface

com.f1.ami.web.AmiWebPanelPlugin

Properties

ami.web.panels=comma_delimited_list_of_fully_qualified_java_class_names

Custom JavaScript

Overview

AmiGuiService - A mechanism for bridging AmiScript to/from JavaScript. This plugin enables JavaScript code to be accessible from AmiScript, through a well-defined AmiScript API. It's bidirectional:

  • Calls to JavaScript from within the browser can, in turn, invoke AmiScript callbacks
  • Calls to AmiScript from within the dashboard can, in turn, call JavaScript within the browser.

In order to interact with AmiScript in the webserver and with JavaScript in the web browser, two blocks of adapter code must be written. Each of these two blocks of code are started as singletons which communicate over the AMI HTTP[S] transport (this transport is, however, transparent to you when building these adapters). The webserver's singleton is one per user session and the JavaScript singleton object is started when the browser loads. These two objects are responsible for communication between their respective environments and each other:

Deep Dive

Initializing

  1. First, a Java plugin implementing the com.f1.ami.web.guiplugin.AmiWebGuiServicePlugin is initiated when the Ami Web Server starts up. The class name must be specified in the ami.guiservice.plugins property. Note, that only one instance is started up per JVM.
  2. Each time a user logs in, the AmiWebGuiServicePlugin::createGuiIntegrationAdapter is called which returns a custom class implementing the AmiWebGuiServiceAdapter interface.
  3. Each time the page is refreshed, including on initial login, the following methods are called on the AmiWebGuiServiceAdapter (These methods initialize the browser's JavaScript environment):
Methods Description
getJavascriptLibraries() Your adapter returns a list of libraries to load. This tells the browser which libraries to load, if any, for this custom adapter.
getJavascriptInitialization() Your adapter should return JavaScript to be executed in the browser at pageLoad and allows for any custom initialization.
getJavascriptNewInstance() Your adapter must return the JavaScript necessary for generating a JavaScript singleton that will receive/send messages. This JavaScript must implement a function called registerAmiGuiServicePeer(peer) which, typically, just stores the peer argument in a member variable for later use.

Registering AmiScript API

  • Declaring methods that can be called from AmiScript.

Within the AmiScript environment a single object will be available that represents the singleton. Similar to the session object which implements the Session AmiScript class, this object will have a predetermined name, class name and available methods. Here are how those are defined:

Methods Description
AmiWebGuiServiceAdapter::getAmiscriptClassname() Your adapter should returns the name of the class that is represented by the singleton (analagous to Session). The singleton object will have the same name but prefixed with two underbars (__).
AmiWebGuiServiceAdapter::getAmiScriptMethods() Your adapter should returns a list of methods that can be called on the custom singleton
  • Declaring AmiScript callbacks.

The callbacks allow for dashboard developers to declare their own AmiScript that gets executed when the callback is invoked. The dashboard developers can edit the callbacks under dashboard -> Custom Gui Service Callbacks -> <Your custom service>

Method Description
AmiWebGuiServiceAdapter::getAmiScriptCallbacks() Your adapter should return a list of callbacks available for overriding.

Binding it together

AmiScript to Javascript (See Registering AmiScript API.1 for registering methods that can be called in AmiScript):

  1. A user invokes a AmiScript method on your custom singleton.

  2. When a dashboard developer calls the custom AmiScript API you've provided in step (a), then AmiWebGuiServiceAdapter::onAmiScriptMethod is called. This allows for you to do any validation, data massaging, etc before sending off to JavaScript.

  3. Call executeJavascriptCallback on the supplied peer. Here is an example of steps 2 and 3, skipping validation, data massaging:

    1
    2
    3
    4
    public Object onAmiScriptMethod(String name, Object[] args, AmiWebGuiServiceAdapterPeer peer) {
        peer.executeJavascriptCallback(name, args);
        return null;
    }
    
  4. The Javascript singleton (as defined in Initializing.3.C) will have the method and arguments called on it

JavaScript to AmiScript (See Registering AmiScript API.2 for registering callbacks inside AmiScript):

  1. A JavaScript function is called on the singleton.

  2. The JavaScript singleton's method should then call:

    peer.sendToServer(methodName,arg1,arg2,...);
    

    (Note: the peer is supplied on startup in the registerAmiGuiServicePeer function)

  3. The backend Java adapter's AmiWebGuiServiceAdapter:: onCallFromJavascript(...) is called. At this point, your adapter can do validation, data massaging, etc. before passing off to AmiScript

  4. Call executeAmiScriptCallback on the supplied peer. Here is an example of steps 3 and 4, skipping validation, data massaging, etc:

    public void onCallFromJavascript(String name, Object args[], AmiWebGuiServiceAdapterPeer peer) { peer.executeAmiScriptCallback(name, args);}
    
  5. AmiScript call back is executed

Full Example

The following example shows a simple example of calling a method on JavaScript from AmiScript, and invoking an AmiScript callback from JavaScript.

Specifically, the example declares an AmiScript custom object called __GuiSample of type GuiSample with a single method called getDate(String) and a single callback called onDateDetermined(...). When the getDate(...) AmiScript is executed, the user is presented with a native JavaScript alert and then the current date from the browser is sent back to the AmiScript via the onDateDetermined(..) callback.

Note, that in this simple example the JavaScript class is defined inside the getJavascriptInitialization() method but generally this would be declared in a dedicated JavaScript library and loaded via getJavascriptLibraries().

local.properties:

ami.guiservice.plugins=samples.AmiWebGuiServicePlugin_Sample

AmiWebGuiServicePlugin_Sample.java:

package samples;

import com.f1.ami.web.AmiWebGuiServiceAdapter;
import com.f1.ami.web.AmiWebGuiServicePlugin;
import com.f1.ami.web.AmiWebService;
import com.f1.container.ContainerTools;
import com.f1.utils.PropertyController;

public class AmiWebGuiServicePlugin_Sample implements AmiWebGuiServicePlugin {

        @Override
        public void init(ContainerTools tools, PropertyController props) {
        }

        @Override
        public String getPluginId() {
                return "GUISAMPLE";
        }

        @Override
        public AmiWebGuiServiceAdapter createGuiIntegrationAdapter(AmiWebService service) {
                return new AmiWebGuiServiceAdapter_Sample();
        }
}

AmiWebGuiServiceAdapter_Sample.java:

package samples;

import java.util.Collections;
import java.util.List;

import com.f1.ami.web.AmiWebGuiServiceAdapter;
import com.f1.ami.web.AmiWebGuiServiceAdapterPeer;
import com.f1.utils.CH;
import com.f1.utils.structs.table.derived.ParamsDefinition;

public class AmiWebGuiServiceAdapter_Sample implements AmiWebGuiServiceAdapter {

        private AmiWebGuiServiceAdapterPeer peer;

        @Override
        public void init(AmiWebGuiServiceAdapterPeer peer) {
                this.peer = peer;
        }

        @Override
        public String getGuiServiceId() {
                return "GUISAMPLE";
        }

        @Override
        public String getDescription() {
                return "Gui Sample";
        }

        @Override
        public String getAmiscriptClassname() {
                return "GuiSample";
        }

        @Override
        public List<ParamsDefinition> getAmiscriptMethods() {
                return CH.l(new ParamsDefinition("getDate", Object.class, "String message"));
        }

        @Override
        public Object onAmiScriptMethod(String name, Object[] args) {
                this.peer.executeJavascriptCallback(name, args);
                return null;
        }

        @Override
        public List<ParamsDefinition> getAmiScriptCallbacks() {
                return CH.l(new ParamsDefinition("onDateDetermined", Object.class, "String value"));
        }

        @Override
        public void onCallFromJavascript(String name, Object args[]) {
                this.peer.executeAmiScriptCallback(name, args);
        }

        @Override
        public String getJavascriptInitialization() {
                StringBuilder js = new StringBuilder();
                js.append("function GuiSampleJavascript(){}\n");

 js.append("GuiSampleJavascript.prototype.registerAmiGuiServicePeer=function(peer){this.peer=peer;}\n");

 js.append("GuiSampleJavascript.prototype.getDate=function(message){alert(message);this.peer.sendToServer('onDateDetermined',Date.now());}\n");
                return js.toString();
        }

        @Override
        public String getJavascriptNewInstance() {
                return "new GuiSampleJavascript()";
        }

        @Override
        public List<String> getJavascriptLibraries() {
                return Collections.EMPTY_LIST;
        }
}

Inside the dashboard, create a new html panel, add a button and set the button's script to:

__GuiSample.getDate("Getting your local date!");

Inside the dashboard, set the AmScript under Dashboard -> Custom Gui Service Callbacks -> Gui Sample Callbacks... -> onDateDetermined tab to the following:

session.alert("Browser's timestamp is: "+ value);

Running the example:

Clicking the button, will now generate a JavaScript alert saying "Getting your local date!" and then an AmiScript alert displaying "Browser's timestamp is: <unixtimestamp>"