Using MadFast Web UI JS library (experimental)

MadFast Web UI is built by a JavaScript build pipeline (webpack, gulp, babel, uglify) and packaged with the embedded server for distribution. Direct reusing and modifying the Web UI original sources is not feasible because of the necessity for these package steps.

This document describes a highly experimental approach to create and use an extension library allowing users to extend the Web UI without the need for setting up this pipeline.

Please note that this is an experimental development direction with expected incompatible changes in future releases. Please contact us if you plan to use this extension library to discuss your use case and compatibility requirements.

Key concepts

The Hello World example

Directory data/jsclient-additional/helloworld contains a basic example (a single HTML file referring to the facade and invoking functionality from the Web UI).

The file content:

<!DOCTYPE html>
<html lang='en'>
<head>
    <!-- WARNING! This example uses highly experimental functionality, expect breaking changes! -->

    <!-- Workaround for Font Awesome loading -->
    <!-- Expect problems when exposing MadFast on a non-root URI through a reverse proxy -->
    <!-- See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base -->
    <base href="/">
    <!-- End of workaround -->

    <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="shortcut icon" href="img/mfss-32.png" type="image/x-icon" />

    <title>MadFast client library - Hello World example</title>

    <!-- Include the packed Web UI facade -->
    <script src="dist/common.bundle.js"></script>
    <script src="dist/jsclient.bundle.js"></script>

    <!-- The actual script -->
    <script>
        // Get MadFast WebUI client library
        var M = window['EXPERIMENTAL-MADFAST-WEBUI'];

        console.log('MadFast WebUI client library facade:', M);

        // jQuery shipped in WebUI, acquire it
        var $ = M.$;

        // This is a component from WebUI for modal dialog handling
        var modals = M.ui.modals;

        $(function() {
            // Show a modal dialog with MarkDown content
            // First argument of this utility method is the dialog title
            // Second is the content to be displayed in MarkDown format
            modals.showMd(
                'Hello World dialog title', 
                '# Hello World\nDialog `markdown` content.\n\n'
                + 'This dialog is displayed using'
                + ' the [MadFast](https://disco.chemaxon.com/products/madfast)'
                + ' Web UI JavaScript client library.'
            );
        });
    </script>
</head>
<body>
</body>
</html>

To use launch

bin/gui.sh -additionalresourcedir data/jsclient-additional/helloworld/ -page additional/index.html

Breakdown of the command line arguments:

Parameter Description
-additionalresourcedir <DIRECTORY> Directory to be exposed under URL additional/ by the embedded server
... data/jsclient-additional/helloworld/ Content (a single HTML file) for this example
-page <LOCATION> After embedded server startup try to open a browser with the specified location
... additional/index.html The exposed content from the specified directory

After server loading a page should open with a modal dialog.

Dialog shown

The Hello World example through rawfiles

The same example can be used through the experimental raw file handling functionality.

Upload rawfile through REST API

Launch a default server with no browser window display (the custom content is not available on startup; we will upload it through the REST API):

bin/gui.sh -port 8085 -nobrowse

Then fire a POST request with curl to expose the index.html mentioned above as a new raw file:

curl -X PUT \
    -F contenttype=text/html \
    -F "description=Hello World example as a raw file resource" \
    -F file=@data/jsclient-additional/helloworld/index.html \
    http://localhost:8085/rest/experimental-rawfiles/hello-world-example.html | python -m json.tool

The response is expected to contain metadata about the new resource:

{
    "contenttype": "text/html",
    "description": "Hello World example as a raw file resource",
    "name": "hello-world-example.html",
    "size": 1847,
    "time": 0,
    "url": "rest/experimental-rawfiles/hello-world-example.html"
}

View the content through endpoint rest/expreimental-rawfiles-content by opening URL http://localhost:8085/rest/expreimental-rawfiles-content/hello-world-example.html:

Dialog shown

Specify rawfile through command line arguments

Raw file resources can be specified upon server startup using command line option -rawfile <SPEC>:

bin/gui.sh -port 8085 \
    -rawfile "-file:data/jsclient-additional/helloworld/index.html:-name:hello-world-example.html:-contenttype:text/html:-description:Hello World example as a raw file resource" \
    -page rest/experimental-rawfiles-content/hello-world-example.html

Since the custom content is available upon server startup we can instruct the server to open it in a browser. The state of the server on startup is the same as in the above example after uploading the content using the REST API.

Real time similarity search example

Directory data/jsclient-additional/search contains a real time similarity search example derived from the one provided in the Web UI. To use we need to prepare a MadFast server with valid molecules/descriptors. Launch

# Prepare data
# Import molecules and IDs
bin/createMms.sh \
    -in data/molecules/vitamins/vitamins.smi \
    -name vitamins-name.bin -out vitamins-mms.bin
# Calculate CFP7 descriptors
bin/buildStorage.sh \
    -context createSimpleCfp7Context \
    -in data/molecules/vitamins/vitamins.smi \
    -out vitamins-cfp7.bin
# Launch server with the imported vitamins dataset and the custom search example
bin/gui.sh \
    -additionalresourcedir data/jsclient-additional/search/ \
    -page "additional/simsearch.html?ref=rest/descriptors/vita-cfp7&src=CN1C=NC2=C1C(=O)N(C)C(=O)N2C" \
    -mols -name:vitamins:-mms:vitamins-mms.bin:-mid:vitamins-name.bin \
    -desc -desc:vitamins-cfp7.bin:-mols:vitamins:-name:vita-cfp7

The following files are relevant for this example:

File upload example

A basic file upload form is available in directory data/jsclient-additional/upload-form.

To launch invoke

bin/gui.sh -additionalresourcedir data/jsclient-additional/upload-form/ -page additional/index.html

Currently only the raw REST API response JSON is displayed after uploading.

Reference: JS client library facade source

As a reference the source of the facade object is included here:

"use strict";

/**
 * Experimental JS client library for MadFast Web UI JS components.
 * 
 * For details see document "doc/using-webui-js-library.html" and the supplementary examples in 
 * "data/jsclient-additional/" directory of the MadFast distribution.
 *
 * This code is highly experimental, use with extreme caution - incompatible changes are expected.
 *
 * Aims (highly work in progress):
 *  - Include all CSS used by MadFast Web UI
 *  - Expose components used by the Web UI
 *  - Allow the composition of custom WebUI pages with WebUI components without the need of a complete build pipeline
 *
 * WARNING! This source is provided for reference only, currently it is not possible to build it outside ChemAxon.
 */

// 3rd party dependencies
import d3 from '../deps/d3.js';
import '../deps/bootstrap-deps.js';
import '../deps/font-awesome.js';
import '../deps/jquery-ui-deps.js';
import _ from '../common/lodash-mod.js';
import crossfilter from '../deps/crossfilter.js';
import chartjs from '../deps/chart.js';

// Common, shared functionalities
import * as pageparam from '../common/pageparam.js';
import createSimpleEvent from '../common/simple-event.js';
import * as formatting from '../common/formatting.js';
import * as xfutils from '../common/xf-utils.js';
import naiveCrossfilter from '../common/naive-crossfilter.js';

// API client layer
import * as dataaccess from '../api-client/dataaccess.js';
import * as network from '../api-client/network.js';
import * as createAsyncClient from '../api-client/async-client.js';
import createDescCtx from '../api-client/similarity-search-context.js';

// Various UI components
import * as knnTable from '../knn/knn-table.js';

import createResourceListPage from '../landing/resource-list.js';

import * as pathes from '../stats/datapath.js';
import newTableCreator from '../stats/xf-text-table.js';
import newWordCloudCreator from '../stats/xf-word-cloud.js';
import newScatterCreator from '../stats/xf-scatter.js';

import addDist from '../ui/ui-dissimilarity-distribution.js';
import addHisto from '../ui/ui-draw-histo.js';
import addMss from '../ui/ui-most-similars.js';
import addPicklist from '../ui/ui-picklist.js';
import addSketcher from '../ui/ui-sketcher.js';
import addSliderTo from '../ui/ui-slider.js';
import colors from '../ui/colors.js';
import createGrid from '../ui/ui-slickgrid.js';
import createLayout from '../ui/ui-basic-layout.js';
import createMessager from '../ui/ui-messager.js';
import createUiComponent from '../ui/ui-component.js';
import * as dropdown from '../ui/ui-dropdown.js';
import initTaskbar from '../ui/ui-taskbar.js';
import * as modals from '../ui/ui-modals.js';
import newMolviewSettings from '../ui/ui-molview-settings.js';
import resizeWatch from '../ui/ui-resize-watch.js';
import * as topmsg from '../ui/ui-topmsg.js';
import * as uicommon from '../ui/ui-common.js';
import * as xfhisto from '../ui/ui-xfilt-histo.js';

import addMsketchInput from '../ui/ui-msketch-input.js';
import addSrcInput from '../ui/ui-src-input.js';
import addStaticgridTo from '../ui/ui-static-grid.js';

import '../molecules/molecules.css';
import '../profres/profres.css';
import '../simsearch/simsearch.css';
import '../ui/cards.css';

var facade = {
    // lodash with some custom extensions, see https://lodash.com/
    _: _,

    // jQuery, see https://jquery.com/
    $: $,

    // D3.js v4, see https://d3js.org/
    d3: d3,

    // Crossfilter community fork, see http://crossfilter.github.io/crossfilter/
    crossfilter: crossfilter,

    // Chart.js, see https://www.chartjs.org/
    chartjs: chartjs, 

    // Web UI modules
    common: {
        createSimpleEvent: createSimpleEvent,
        formatting : formatting,
        pageparam: pageparam,
        xfutils : xfutils,
        naiveCrossfilter : naiveCrossfilter
    },
    apiclient: {
        dataaccess: dataaccess,
        network: network,
        createAsyncClient: createAsyncClient,
        createDescCtx: createDescCtx
    },
    knn: {
        knnTable: knnTable
    },
    landing: {
        createResourceListPage: createResourceListPage
    },
    stats: {
        newScatterCreator: newScatterCreator,
        newTableCreator: newTableCreator,
        newWordCloudCreator: newWordCloudCreator,
        pathes: pathes
    },
    ui: {
        addDist: addDist,
        addHisto: addHisto,
        addMsketchInput: addMsketchInput,
        addMss: addMss,
        addPicklist: addPicklist,
        addSketcher: addSketcher,
        addSrcInput: addSrcInput,
        addStaticgridTo: addStaticgridTo,
        addSliderTo: addSliderTo,
        colors: colors,
        createGrid: createGrid,
        createLayout: createLayout,
        createMessager: createMessager,
        createUiComponent: createUiComponent,
        dropdown: dropdown,
        initTaskbar: initTaskbar,
        modals: modals,
        newMolviewSettings: newMolviewSettings,
        resizeWatch: resizeWatch,
        topmsg: topmsg,
        uicommon: uicommon,
        xfhisto: xfhisto
    }
};

// export for js client
window['EXPERIMENTAL-MADFAST-WEBUI'] = facade;

// export for testing
export default facade;