Skip to content
Snippets Groups Projects

Plugin README

A plugin needs to contain three files.

  • Manifest JSON
  • template HTML
  • script JS

These files need to be served by GET requests over HTTP with appropriate CORS header. If your application requires a backend, it is strongly recommended to host these three files with your backend.


Manifest JSON

The manifest JSON file describes the metadata associated with the plugin.

{
  "name":"fzj.xg.helloWorld",
  "displayName": "Hello World - my first plugin",
  "templateURL":"http://LINK-TO-YOUR-PLUGIN-TEMPLATE/template.html",
  "scriptURL":"http://LINK-TO-YOUR-PLUGIN-SCRIPT/script.js",
  "initState":{
    "key1": "value1",
    "key2" : {
      "nestedKey1" : "nestedValue1"
    }
  },
  "initStateUrl": "http://LINK-TO-PLUGIN-STATE",
  "persistency": false
}

NB

  • Plugin name must be unique globally. To prevent plugin name clashing, please adhere to the convention of naming your package AFFILIATION.AUTHORNAME.PACKAGENAME.
  • the initState object and initStateUrl will be available prior to the evaluation of script.js, and will populate the objects interactiveViewer.pluginControl[MANIFEST.name].initState and interactiveViewer.pluginControl[MANIFEST.name].initStateUrl respectively.

Template HTML

The template HTML file describes the HTML view that will be rendered in the widget.

<form>
  <div class = "input-group">
    <span class = "input-group-addon">Area 1</span>
    <input type = "text" id = "fzj.xg.helloWorld.area1" name = "fzj.xg.helloWorld.area1" class = "form-control" placeholder="Select a region" value = "">
  </div>

  <div class = "input-group">
    <span class = "input-group-addon">Area 2</span>
    <input type = "text" id = "fzj.xg.helloWorld.area2" name = "fzj.xg.helloWorld.area2" class = "form-control" placeholder="Select a region" value = "">
  </div>

  <hr class = "col-md-10">

  <div class = "col-md-12">
    Select genes of interest:
  </div>
  <div class = "input-group">
    <input type = "text" id = "fzj.xg.helloWorld.genes" name = "fzj.xg.helloWorld.genes" class = "form-control" placeholder = "Genes of interest ...">
    <span class = "input-group-btn">
      <button id = "fzj.xg.helloWorld.addgenes" name = "fzj.xg.helloWorld.addgenes" class = "btn btn-default" type = "button">Add</button>
    </span>
  </div>

  <hr class = "col-md-10">

  <button id = "fzj.xg.helloWorld.submit" name = "fzj.xg.helloWorld.submit" type = "button" class = "btn btn-default btn-block">Submit</button>

  <hr class = "col-md-10">

  <div class = "col-md-12" id = "fzj.xg.helloWorld.result">

  </div>
</form>

NB

  • bootstrap 3.3.6 css is already included for templating.
  • keep in mind of the widget width restriction (400px) when crafting the template
  • whilst there are no vertical limits on the widget, contents can be rendered outside the viewport. Consider setting the max-height attribute.
  • your template and script will interact with each other likely via element id. As a result, it is highly recommended that unique id's are used. Please adhere to the convention: AFFILIATION.AUTHOR.PACKAGENAME.ELEMENTID

Script JS

The script will always be appended after the rendering of the template.

(()=>{
  /* your code here */

  if(interactiveViewer.pluginControl['fzj.xg.helloWorld'].initState){
    /* init plugin with initState */
  }
  
  const submitButton = document.getElemenById('fzj.xg.helloWorld.submit')
  submitButton.addEventListener('click',(ev)=>{
    console.log('submit button was clicked')
  })
})()

NB

  • JS is loaded and executed before the attachment of DOM (template). This is to allow webcomponents have a chance to be loaded. If your script needs the DOM to be attached, use a setTimeout callback to delay script execution.
  • ensure the script is scoped locally, instead of poisoning the global scope
  • for every observable subscription, call unsubscribe() in the onShutdown callback
  • some frameworks such as jquery2, jquery3, react/reactdom and webcomponents can be loaded via interactiveViewer.pluinControl.loadExternalLibraries([LIBRARY_NAME_1, LIBRARY_NAME_2]). if the libraries are loaded, remember to hook interactiveViewer.pluginControl.unloadExternalLibraries([LIBRARY_NAME_1,LIBRARY_NAME_2]) in the onShutdown callback
  • when/if using webcomponents, please be aware that the connectedCallback() and disconnectedCallback() will be called everytime user toggle between floating and docked modes.
  • when user navigate to a new template all existing widgets will be destroyed, unless the persistency is set to true in mannifest.json.
  • for a list of APIs, see plugin_api.md