<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://daq00.triumf.ca/MidasWiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Stefan+Ritt</id>
	<title>MidasWiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://daq00.triumf.ca/MidasWiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Stefan+Ritt"/>
	<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php/Special:Contributions/Stefan_Ritt"/>
	<updated>2026-05-24T19:16:31Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.6</generator>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Install_Script&amp;diff=3612</id>
		<title>Install Script</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Install_Script&amp;diff=3612"/>
		<updated>2026-03-31T10:31:50Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* MIDAS installation via script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== MIDAS installation via script ==&lt;br /&gt;
&lt;br /&gt;
To install MIDAS vis the install script, make sure that following packets are installed:&lt;br /&gt;
&lt;br /&gt;
* git&lt;br /&gt;
* cmake&lt;br /&gt;
* g++ / c++&lt;br /&gt;
* zlib1g-dev&lt;br /&gt;
&lt;br /&gt;
Then paste following command in a terminal or shell prompt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/bin/bash -c &amp;quot;$(curl -sS https://bitbucket.org/tmidas/midas/raw/HEAD/install.sh)&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For an unattended install with default parameters do:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/bin/bash -c &amp;quot;$(curl -sS https://bitbucket.org/tmidas/midas/raw/HEAD/install.sh)&amp;quot; -- -f&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The script will:&lt;br /&gt;
&lt;br /&gt;
* Check if all necessary tools and libraries are installed and break if not&lt;br /&gt;
* Ask for the installation directory, the midas experiment directory and the experiment name&lt;br /&gt;
* Download midas via &#039;&#039;git clone&#039;&#039;&lt;br /&gt;
* Compile midas via &#039;&#039;cmake&#039;&#039;&lt;br /&gt;
* Install midas under the user $MIDASSYS/bin directory where $MIDASSYS is the midas installation directory&lt;br /&gt;
* Add the above directory to the path and put the environment variables MIDASSYS, MIDAS_DIR and MIDAS_EXPT_NAME in your environment&lt;br /&gt;
* Add $MIDASSYS/python to PYTHONPATH&lt;br /&gt;
* Create your experiment directory&lt;br /&gt;
* Load an initial ODB file which configures mhttpd to accept insecure connections through port 8081. Change that later to secure your connections.&lt;br /&gt;
* Install the MIDAS web server &#039;&#039;mhttpd&#039;&#039; as a system daemon via systemctl and start it&lt;br /&gt;
* Start a midas example frontend and the logger&lt;br /&gt;
&lt;br /&gt;
After a successful installation, you should be able to connect to mhttpd via http://localhost:8081 and see your experiment&lt;br /&gt;
&lt;br /&gt;
=== Non-interactive installations ===&lt;br /&gt;
It is also possible to first download the &#039;&#039;&#039;install.sh&#039;&#039;&#039; file using the &#039;&#039;&#039;curl&#039;&#039;&#039; command and then executing it directly with &#039;&#039;&#039;./install.sh&#039;&#039;&#039;. Add&lt;br /&gt;
the flag &amp;quot;-f&amp;quot; to force the installation at the default directories without asking for any user input. This can be useful for automated&lt;br /&gt;
installations such as in a Docker.&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Install_Script&amp;diff=3611</id>
		<title>Install Script</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Install_Script&amp;diff=3611"/>
		<updated>2026-03-31T10:29:51Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* MIDAS installation via script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== MIDAS installation via script ==&lt;br /&gt;
&lt;br /&gt;
To install MIDAS vis the install script, make sure that following packets are installed:&lt;br /&gt;
&lt;br /&gt;
* git&lt;br /&gt;
* cmake&lt;br /&gt;
* g++ / c++&lt;br /&gt;
* zlib1g-dev&lt;br /&gt;
&lt;br /&gt;
Then paste following command in a terminal or shell prompt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/bin/bash -c &amp;quot;$(curl -sS https://bitbucket.org/tmidas/midas/raw/HEAD/install.sh)&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For an unattended install with default parameters do:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/bin/bash -c &amp;quot;$(curl -sS https://bitbucket.org/tmidas/midas/raw/HEAD/install.sh -f)&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The script will:&lt;br /&gt;
&lt;br /&gt;
* Check if all necessary tools and libraries are installed and break if not&lt;br /&gt;
* Ask for the installation directory, the midas experiment directory and the experiment name&lt;br /&gt;
* Download midas via &#039;&#039;git clone&#039;&#039;&lt;br /&gt;
* Compile midas via &#039;&#039;cmake&#039;&#039;&lt;br /&gt;
* Install midas under the user $MIDASSYS/bin directory where $MIDASSYS is the midas installation directory&lt;br /&gt;
* Add the above directory to the path and put the environment variables MIDASSYS, MIDAS_DIR and MIDAS_EXPT_NAME in your environment&lt;br /&gt;
* Add $MIDASSYS/python to PYTHONPATH&lt;br /&gt;
* Create your experiment directory&lt;br /&gt;
* Load an initial ODB file which configures mhttpd to accept insecure connections through port 8081. Change that later to secure your connections.&lt;br /&gt;
* Install the MIDAS web server &#039;&#039;mhttpd&#039;&#039; as a system daemon via systemctl and start it&lt;br /&gt;
* Start a midas example frontend and the logger&lt;br /&gt;
&lt;br /&gt;
After a successful installation, you should be able to connect to mhttpd via http://localhost:8081 and see your experiment&lt;br /&gt;
&lt;br /&gt;
=== Non-interactive installations ===&lt;br /&gt;
It is also possible to first download the &#039;&#039;&#039;install.sh&#039;&#039;&#039; file using the &#039;&#039;&#039;curl&#039;&#039;&#039; command and then executing it directly with &#039;&#039;&#039;./install.sh&#039;&#039;&#039;. Add&lt;br /&gt;
the flag &amp;quot;-f&amp;quot; to force the installation at the default directories without asking for any user input. This can be useful for automated&lt;br /&gt;
installations such as in a Docker.&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Install_Script&amp;diff=3610</id>
		<title>Install Script</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Install_Script&amp;diff=3610"/>
		<updated>2026-03-31T10:29:18Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* MIDAS installation via script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== MIDAS installation via script ==&lt;br /&gt;
&lt;br /&gt;
To install MIDAS vis the install script, make sure that following packets are installed:&lt;br /&gt;
&lt;br /&gt;
* git&lt;br /&gt;
* cmake&lt;br /&gt;
* g++ / c++&lt;br /&gt;
* zlib1g-dev&lt;br /&gt;
&lt;br /&gt;
Then paste following command in a terminal or shell prompt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/bin/bash -c &amp;quot;$(curl -sS https://bitbucket.org/tmidas/midas/raw/HEAD/install.sh)&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For an unattended install with default parameters do:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/bin/bash -c &amp;quot;$(curl -sS https://bitbucket.org/tmidas/midas/raw/HEAD/install.sh -f)&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The script will:&lt;br /&gt;
&lt;br /&gt;
* Check if all necessary tools and libraries are installed and break if not&lt;br /&gt;
* Ask for the installation directory, the midas experiment directory and the experiment name&lt;br /&gt;
* Download midas via &#039;&#039;git clone&#039;&#039;&lt;br /&gt;
* Compile midas via &#039;&#039;cmake&#039;&#039;&lt;br /&gt;
* Install midas under the user $MIDASSYS/bin directory where $MIDASSYS is the midas installation directory&lt;br /&gt;
* Add the above directory to the path and put the environment variables MIDASSYS, MIDAS_DIR and MIDAS_EXPT_NAME in your environment&lt;br /&gt;
* Add $MIDASSYS/python to PYTHONPATH&lt;br /&gt;
* Create your experiment directory&lt;br /&gt;
* Load an initial ODB file which configures mhttpd to accept insecure connections through port 8081. Change that later to secure your connections.&lt;br /&gt;
* Install the MIDAS web server &#039;&#039;mhttpd&#039;&#039; as a system daemon via systemctl and start it&lt;br /&gt;
* Start a midas example frontend and the logger&lt;br /&gt;
&lt;br /&gt;
After a successful installation, you should be able to connect to mhttpd via http://localhost:8081 and see your experiment&lt;br /&gt;
&lt;br /&gt;
=== Non-interactive installations ===&lt;br /&gt;
It is also possible to first download the &#039;&#039;&#039;install.sh&#039;&#039;&#039; file using the &#039;&#039;&#039;curl&#039;&#039;&#039; command and then executing it directly with &#039;&#039;&#039;./install.sh&#039;&#039;&#039;. Add&lt;br /&gt;
the flag &amp;quot;-f&amp;quot; to force the installation at the default directories without asking for any user input. This can be useful for automated&lt;br /&gt;
installations such as in a Docker.&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3599</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3599"/>
		<updated>2026-01-20T11:00:16Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Tabbed Pages */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.&lt;br /&gt;
&lt;br /&gt;
We note that MIDAS has provided a number of different ways of providing custom pages over the years.  A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a &amp;quot;virtual&amp;quot; X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.&lt;br /&gt;
&lt;br /&gt;
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.&lt;br /&gt;
&lt;br /&gt;
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in&lt;br /&gt;
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: With the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new tab/window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
To enable clicks on a menu item that result in an onclick event (no html refresh), one can create and ODB key in the /Custom (or its subdirectories) whose value is a string that does not end with a valid extension (txt|jpg|jpeg|png|gif|pdf|csv|html|js|css). In this case, the string will be encapsulated into and onclick event. For example,&lt;br /&gt;
odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    Click                             alert(1)&lt;br /&gt;
&lt;br /&gt;
Clicking on the menu item &amp;quot;Click&amp;quot; will result in an alert window.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.&lt;br /&gt;
&lt;br /&gt;
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions.  The advantages of using these modb* javascript functions are&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient.  In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call).  It will also require more coding to implement the custom page with only MjsonRPC calls.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Color of gauge bar, &amp;quot;black&amp;quot; if not present. Alternatively one can define color segments like &#039;&#039;&#039;[{0, &#039;green&#039;},{7,&#039;yellow&#039;},{9,&#039;red}]&#039;&#039;&#039; to change the bar color dynamically. If the value is above 0 and blow 7, the bar will be green in this example, yellow between 7 and below 9, and red at or above 9.&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Dynamic ODB paths ==&lt;br /&gt;
&lt;br /&gt;
The path of a custom control is static, meaning it has to be written directly inside the control via &amp;lt;code&amp;gt;data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;lt;/code&amp;gt;. There can be however cases where the path is only known at runtime. For these cases, the path can be a user function which gets called during each update of the control and has to return the current ODB path. This can be useful for equipment values with names. The standard scheme of MIDAS is to have slow control variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Variables&amp;lt;/code&amp;gt; and the names of these variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names&amp;lt;/code&amp;gt;. If there are several different arrays under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;, you might also have something like &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names XXX&amp;lt;/code&amp;gt; where &amp;lt;code&amp;gt;XXX&amp;lt;/code&amp;gt; is the name of the key under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;. If one wants to use a name of a variable rather than its index, the dynamic path function can be uses like in the following example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
   let names;&lt;br /&gt;
&lt;br /&gt;
   function getNames() {&lt;br /&gt;
      mjsonrpc_db_get_value(&amp;quot;/Equipment/.../Settings/Names&amp;quot;).then(function (rpc) {&lt;br /&gt;
         names = rpc.result.data[0];&lt;br /&gt;
         mhttpd_init(&#039;Custom Page Example&#039;);&lt;br /&gt;
      });&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   function getpath(p) {&lt;br /&gt;
      if (mscbNames)&lt;br /&gt;
         return `/Equipment/.../Variables/MSCB[${names.indexOf(p)}]`;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;getNames();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the page is initialized, the function &amp;lt;code&amp;gt;getNames()&amp;lt;/code&amp;gt; is called, which obtains the &amp;lt;code&amp;gt;Names&amp;lt;/code&amp;gt; array from the ODB and stores it into an array. After the names have been received, the function calls the usual &amp;lt;code&amp;gt;mhttpd_init()&amp;lt;/code&amp;gt; function. The &amp;quot;modbvalue&amp;quot; path is now set to &amp;lt;code&amp;gt;data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;lt;/code&amp;gt;. This function does a lookup in the &amp;lt;code&amp;gt;names&amp;lt;/code&amp;gt; array and returns the proper index, which is then used to access the corresponding value in the variables array. This method has the advantage that if the indices of an array change (e.g. by adding new variables at the front), the code does not have to update any index since the indices are calculated dynamically.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Implementing SVGs =&lt;br /&gt;
&lt;br /&gt;
An SVG file loaded on a custom page can be interactively modified using JavaScript.&lt;br /&gt;
This is useful for graphically displaying ODB values in complex service or detector systems.&lt;br /&gt;
&lt;br /&gt;
An SVG file can be embedded like this:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
&amp;lt;object id=&amp;quot;svgObject&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; data=&amp;quot;Midas_helium_example.svg&amp;quot;&amp;gt;&amp;lt;/object&amp;gt; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Objects within the SVG can be modified—for example, by changing their fill color as shown below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
const svgDoc = document.getElementById(&#039;svgObject&#039;).contentDocument; &lt;br /&gt;
const obj_Valve = svgDoc.getElementById(valveID); &lt;br /&gt;
obj_Valve.style.fill = &amp;quot;#00FF00&amp;quot;; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SVG images are stored in XML format. In Inkscape, object properties can be interactively edited using the XML Editor.&lt;br /&gt;
A full example is given below in [[SVGs on Custom Pages]]&lt;br /&gt;
&lt;br /&gt;
= Tabbed Pages =&lt;br /&gt;
&lt;br /&gt;
Once custom pages get complex, it is possible to distribute the controls over several&lt;br /&gt;
pages. The user can then click on individual tabs at the top to switch between the pages.&lt;br /&gt;
This can be implemented following this pattern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mtabs&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab active&amp;quot; data-target=&amp;quot;p1&amp;quot;&amp;gt;First&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab&amp;quot; data-target=&amp;quot;p2&amp;quot;&amp;gt;Second&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab&amp;quot; data-target=&amp;quot;p3&amp;quot;&amp;gt;Third&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p1&amp;quot; class=&amp;quot;mpanel active&amp;quot;&amp;gt;&lt;br /&gt;
   Page 1&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p2&amp;quot; class=&amp;quot;mpanel&amp;quot;&amp;gt;&lt;br /&gt;
   Page 2&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p3&amp;quot; class=&amp;quot;mpanel&amp;quot;&amp;gt;&lt;br /&gt;
   Page 3&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates three pages with three associated tabs &amp;quot;First&amp;quot;, &amp;quot;Second&amp;quot; and &amp;quot;Third&amp;quot;. The &amp;quot;active&amp;quot; flag indicates which tab is active initially. To activate a tab programmatically, one can simulate a mouse click on that tab with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
let tab = document.getElementsByClassName(&#039;mtab&#039;);&lt;br /&gt;
tab[1].click(); // index is 0, 1, 2 for the three pages&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is then what you get:&lt;br /&gt;
&lt;br /&gt;
[[File:Tabbed page.png|frame|left|alt=Example of a tabbed custom page|Figure 1  Example of a tabbed custom page]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 2  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3598</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3598"/>
		<updated>2026-01-20T10:59:20Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Tabbed Pages */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.&lt;br /&gt;
&lt;br /&gt;
We note that MIDAS has provided a number of different ways of providing custom pages over the years.  A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a &amp;quot;virtual&amp;quot; X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.&lt;br /&gt;
&lt;br /&gt;
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.&lt;br /&gt;
&lt;br /&gt;
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in&lt;br /&gt;
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: With the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new tab/window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
To enable clicks on a menu item that result in an onclick event (no html refresh), one can create and ODB key in the /Custom (or its subdirectories) whose value is a string that does not end with a valid extension (txt|jpg|jpeg|png|gif|pdf|csv|html|js|css). In this case, the string will be encapsulated into and onclick event. For example,&lt;br /&gt;
odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    Click                             alert(1)&lt;br /&gt;
&lt;br /&gt;
Clicking on the menu item &amp;quot;Click&amp;quot; will result in an alert window.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.&lt;br /&gt;
&lt;br /&gt;
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions.  The advantages of using these modb* javascript functions are&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient.  In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call).  It will also require more coding to implement the custom page with only MjsonRPC calls.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Color of gauge bar, &amp;quot;black&amp;quot; if not present. Alternatively one can define color segments like &#039;&#039;&#039;[{0, &#039;green&#039;},{7,&#039;yellow&#039;},{9,&#039;red}]&#039;&#039;&#039; to change the bar color dynamically. If the value is above 0 and blow 7, the bar will be green in this example, yellow between 7 and below 9, and red at or above 9.&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Dynamic ODB paths ==&lt;br /&gt;
&lt;br /&gt;
The path of a custom control is static, meaning it has to be written directly inside the control via &amp;lt;code&amp;gt;data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;lt;/code&amp;gt;. There can be however cases where the path is only known at runtime. For these cases, the path can be a user function which gets called during each update of the control and has to return the current ODB path. This can be useful for equipment values with names. The standard scheme of MIDAS is to have slow control variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Variables&amp;lt;/code&amp;gt; and the names of these variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names&amp;lt;/code&amp;gt;. If there are several different arrays under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;, you might also have something like &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names XXX&amp;lt;/code&amp;gt; where &amp;lt;code&amp;gt;XXX&amp;lt;/code&amp;gt; is the name of the key under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;. If one wants to use a name of a variable rather than its index, the dynamic path function can be uses like in the following example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
   let names;&lt;br /&gt;
&lt;br /&gt;
   function getNames() {&lt;br /&gt;
      mjsonrpc_db_get_value(&amp;quot;/Equipment/.../Settings/Names&amp;quot;).then(function (rpc) {&lt;br /&gt;
         names = rpc.result.data[0];&lt;br /&gt;
         mhttpd_init(&#039;Custom Page Example&#039;);&lt;br /&gt;
      });&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   function getpath(p) {&lt;br /&gt;
      if (mscbNames)&lt;br /&gt;
         return `/Equipment/.../Variables/MSCB[${names.indexOf(p)}]`;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;getNames();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the page is initialized, the function &amp;lt;code&amp;gt;getNames()&amp;lt;/code&amp;gt; is called, which obtains the &amp;lt;code&amp;gt;Names&amp;lt;/code&amp;gt; array from the ODB and stores it into an array. After the names have been received, the function calls the usual &amp;lt;code&amp;gt;mhttpd_init()&amp;lt;/code&amp;gt; function. The &amp;quot;modbvalue&amp;quot; path is now set to &amp;lt;code&amp;gt;data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;lt;/code&amp;gt;. This function does a lookup in the &amp;lt;code&amp;gt;names&amp;lt;/code&amp;gt; array and returns the proper index, which is then used to access the corresponding value in the variables array. This method has the advantage that if the indices of an array change (e.g. by adding new variables at the front), the code does not have to update any index since the indices are calculated dynamically.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Implementing SVGs =&lt;br /&gt;
&lt;br /&gt;
An SVG file loaded on a custom page can be interactively modified using JavaScript.&lt;br /&gt;
This is useful for graphically displaying ODB values in complex service or detector systems.&lt;br /&gt;
&lt;br /&gt;
An SVG file can be embedded like this:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
&amp;lt;object id=&amp;quot;svgObject&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; data=&amp;quot;Midas_helium_example.svg&amp;quot;&amp;gt;&amp;lt;/object&amp;gt; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Objects within the SVG can be modified—for example, by changing their fill color as shown below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
const svgDoc = document.getElementById(&#039;svgObject&#039;).contentDocument; &lt;br /&gt;
const obj_Valve = svgDoc.getElementById(valveID); &lt;br /&gt;
obj_Valve.style.fill = &amp;quot;#00FF00&amp;quot;; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SVG images are stored in XML format. In Inkscape, object properties can be interactively edited using the XML Editor.&lt;br /&gt;
A full example is given below in [[SVGs on Custom Pages]]&lt;br /&gt;
&lt;br /&gt;
= Tabbed Pages =&lt;br /&gt;
&lt;br /&gt;
Once custom pages get complex, it is possible to distribute the controls over several&lt;br /&gt;
pages. The user can then click on individual tabs at the top to switch between the pages.&lt;br /&gt;
This can be implemented following this pattern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mtabs&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab active&amp;quot; data-target=&amp;quot;p1&amp;quot;&amp;gt;First&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab&amp;quot; data-target=&amp;quot;p2&amp;quot;&amp;gt;Second&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab&amp;quot; data-target=&amp;quot;p3&amp;quot;&amp;gt;Third&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p1&amp;quot; class=&amp;quot;mpanel active&amp;quot;&amp;gt;&lt;br /&gt;
   Page 1&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p2&amp;quot; class=&amp;quot;mpanel&amp;quot;&amp;gt;&lt;br /&gt;
   Page 2&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p3&amp;quot; class=&amp;quot;mpanel&amp;quot;&amp;gt;&lt;br /&gt;
   Page 3&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates three pages with three associated tabs &amp;quot;First&amp;quot;, &amp;quot;Second&amp;quot; and &amp;quot;Third&amp;quot;. The &amp;quot;active&amp;quot; flag indicates which tab is active initially. To activate a tab programmatically, one can simulate a mouse click on that tab with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
let tab = document.getElementsByClassName(&#039;mtab&#039;);&lt;br /&gt;
tab[1].click(); // index is 0, 1, 2 for the three pages&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is then what you get:&lt;br /&gt;
&lt;br /&gt;
[[File:Tabbed page.png|frame|left|alt=Example of a tabbed custom page|Figure 1  Example of a tabbed custom page]]&lt;br /&gt;
{{clear}}&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 2  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3597</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3597"/>
		<updated>2026-01-20T10:58:00Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.&lt;br /&gt;
&lt;br /&gt;
We note that MIDAS has provided a number of different ways of providing custom pages over the years.  A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a &amp;quot;virtual&amp;quot; X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.&lt;br /&gt;
&lt;br /&gt;
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.&lt;br /&gt;
&lt;br /&gt;
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in&lt;br /&gt;
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: With the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new tab/window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
To enable clicks on a menu item that result in an onclick event (no html refresh), one can create and ODB key in the /Custom (or its subdirectories) whose value is a string that does not end with a valid extension (txt|jpg|jpeg|png|gif|pdf|csv|html|js|css). In this case, the string will be encapsulated into and onclick event. For example,&lt;br /&gt;
odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    Click                             alert(1)&lt;br /&gt;
&lt;br /&gt;
Clicking on the menu item &amp;quot;Click&amp;quot; will result in an alert window.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.&lt;br /&gt;
&lt;br /&gt;
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions.  The advantages of using these modb* javascript functions are&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient.  In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call).  It will also require more coding to implement the custom page with only MjsonRPC calls.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Color of gauge bar, &amp;quot;black&amp;quot; if not present. Alternatively one can define color segments like &#039;&#039;&#039;[{0, &#039;green&#039;},{7,&#039;yellow&#039;},{9,&#039;red}]&#039;&#039;&#039; to change the bar color dynamically. If the value is above 0 and blow 7, the bar will be green in this example, yellow between 7 and below 9, and red at or above 9.&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Dynamic ODB paths ==&lt;br /&gt;
&lt;br /&gt;
The path of a custom control is static, meaning it has to be written directly inside the control via &amp;lt;code&amp;gt;data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;lt;/code&amp;gt;. There can be however cases where the path is only known at runtime. For these cases, the path can be a user function which gets called during each update of the control and has to return the current ODB path. This can be useful for equipment values with names. The standard scheme of MIDAS is to have slow control variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Variables&amp;lt;/code&amp;gt; and the names of these variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names&amp;lt;/code&amp;gt;. If there are several different arrays under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;, you might also have something like &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names XXX&amp;lt;/code&amp;gt; where &amp;lt;code&amp;gt;XXX&amp;lt;/code&amp;gt; is the name of the key under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;. If one wants to use a name of a variable rather than its index, the dynamic path function can be uses like in the following example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
   let names;&lt;br /&gt;
&lt;br /&gt;
   function getNames() {&lt;br /&gt;
      mjsonrpc_db_get_value(&amp;quot;/Equipment/.../Settings/Names&amp;quot;).then(function (rpc) {&lt;br /&gt;
         names = rpc.result.data[0];&lt;br /&gt;
         mhttpd_init(&#039;Custom Page Example&#039;);&lt;br /&gt;
      });&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   function getpath(p) {&lt;br /&gt;
      if (mscbNames)&lt;br /&gt;
         return `/Equipment/.../Variables/MSCB[${names.indexOf(p)}]`;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;getNames();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the page is initialized, the function &amp;lt;code&amp;gt;getNames()&amp;lt;/code&amp;gt; is called, which obtains the &amp;lt;code&amp;gt;Names&amp;lt;/code&amp;gt; array from the ODB and stores it into an array. After the names have been received, the function calls the usual &amp;lt;code&amp;gt;mhttpd_init()&amp;lt;/code&amp;gt; function. The &amp;quot;modbvalue&amp;quot; path is now set to &amp;lt;code&amp;gt;data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;lt;/code&amp;gt;. This function does a lookup in the &amp;lt;code&amp;gt;names&amp;lt;/code&amp;gt; array and returns the proper index, which is then used to access the corresponding value in the variables array. This method has the advantage that if the indices of an array change (e.g. by adding new variables at the front), the code does not have to update any index since the indices are calculated dynamically.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Implementing SVGs =&lt;br /&gt;
&lt;br /&gt;
An SVG file loaded on a custom page can be interactively modified using JavaScript.&lt;br /&gt;
This is useful for graphically displaying ODB values in complex service or detector systems.&lt;br /&gt;
&lt;br /&gt;
An SVG file can be embedded like this:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
&amp;lt;object id=&amp;quot;svgObject&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; data=&amp;quot;Midas_helium_example.svg&amp;quot;&amp;gt;&amp;lt;/object&amp;gt; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Objects within the SVG can be modified—for example, by changing their fill color as shown below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
const svgDoc = document.getElementById(&#039;svgObject&#039;).contentDocument; &lt;br /&gt;
const obj_Valve = svgDoc.getElementById(valveID); &lt;br /&gt;
obj_Valve.style.fill = &amp;quot;#00FF00&amp;quot;; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SVG images are stored in XML format. In Inkscape, object properties can be interactively edited using the XML Editor.&lt;br /&gt;
A full example is given below in [[SVGs on Custom Pages]]&lt;br /&gt;
&lt;br /&gt;
= Tabbed Pages =&lt;br /&gt;
&lt;br /&gt;
Once custom pages get complex, it is possible to distribute the controls over several&lt;br /&gt;
pages. The user can then click on individual tabs at the top to switch between the pages.&lt;br /&gt;
This can be implemented following this pattern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mtabs&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab active&amp;quot; data-target=&amp;quot;p1&amp;quot;&amp;gt;First&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab&amp;quot; data-target=&amp;quot;p2&amp;quot;&amp;gt;Second&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab&amp;quot; data-target=&amp;quot;p3&amp;quot;&amp;gt;Third&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p1&amp;quot; class=&amp;quot;mpanel active&amp;quot;&amp;gt;&lt;br /&gt;
   Page 1&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p2&amp;quot; class=&amp;quot;mpanel&amp;quot;&amp;gt;&lt;br /&gt;
   Page 2&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p3&amp;quot; class=&amp;quot;mpanel&amp;quot;&amp;gt;&lt;br /&gt;
   Page 3&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates three pages with three associated tabs &amp;quot;First&amp;quot;, &amp;quot;Second&amp;quot; and &amp;quot;Third&amp;quot;. The &amp;quot;active&amp;quot; flag indicates which tab is active initially. To activate a tab programmatically, one can simulate a mouse click on that tab with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
let tab = document.getElementsByClassName(&#039;mtab&#039;);&lt;br /&gt;
tab[1].click(); // index is 0, 1, 2 for the three pages&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is then what you get:&lt;br /&gt;
&lt;br /&gt;
[[File:Tabbed page.png|frame|left|alt=Example of a tabbed custom page|Example of a tabbed custom page]]&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 2  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3596</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3596"/>
		<updated>2026-01-20T10:57:06Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Tabbed Pages */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.&lt;br /&gt;
&lt;br /&gt;
We note that MIDAS has provided a number of different ways of providing custom pages over the years.  A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a &amp;quot;virtual&amp;quot; X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.&lt;br /&gt;
&lt;br /&gt;
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.&lt;br /&gt;
&lt;br /&gt;
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in&lt;br /&gt;
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: With the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new tab/window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
To enable clicks on a menu item that result in an onclick event (no html refresh), one can create and ODB key in the /Custom (or its subdirectories) whose value is a string that does not end with a valid extension (txt|jpg|jpeg|png|gif|pdf|csv|html|js|css). In this case, the string will be encapsulated into and onclick event. For example,&lt;br /&gt;
odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    Click                             alert(1)&lt;br /&gt;
&lt;br /&gt;
Clicking on the menu item &amp;quot;Click&amp;quot; will result in an alert window.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.&lt;br /&gt;
&lt;br /&gt;
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions.  The advantages of using these modb* javascript functions are&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient.  In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call).  It will also require more coding to implement the custom page with only MjsonRPC calls.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Color of gauge bar, &amp;quot;black&amp;quot; if not present. Alternatively one can define color segments like &#039;&#039;&#039;[{0, &#039;green&#039;},{7,&#039;yellow&#039;},{9,&#039;red}]&#039;&#039;&#039; to change the bar color dynamically. If the value is above 0 and blow 7, the bar will be green in this example, yellow between 7 and below 9, and red at or above 9.&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Dynamic ODB paths ==&lt;br /&gt;
&lt;br /&gt;
The path of a custom control is static, meaning it has to be written directly inside the control via &amp;lt;code&amp;gt;data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;lt;/code&amp;gt;. There can be however cases where the path is only known at runtime. For these cases, the path can be a user function which gets called during each update of the control and has to return the current ODB path. This can be useful for equipment values with names. The standard scheme of MIDAS is to have slow control variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Variables&amp;lt;/code&amp;gt; and the names of these variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names&amp;lt;/code&amp;gt;. If there are several different arrays under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;, you might also have something like &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names XXX&amp;lt;/code&amp;gt; where &amp;lt;code&amp;gt;XXX&amp;lt;/code&amp;gt; is the name of the key under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;. If one wants to use a name of a variable rather than its index, the dynamic path function can be uses like in the following example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
   let names;&lt;br /&gt;
&lt;br /&gt;
   function getNames() {&lt;br /&gt;
      mjsonrpc_db_get_value(&amp;quot;/Equipment/.../Settings/Names&amp;quot;).then(function (rpc) {&lt;br /&gt;
         names = rpc.result.data[0];&lt;br /&gt;
         mhttpd_init(&#039;Custom Page Example&#039;);&lt;br /&gt;
      });&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   function getpath(p) {&lt;br /&gt;
      if (mscbNames)&lt;br /&gt;
         return `/Equipment/.../Variables/MSCB[${names.indexOf(p)}]`;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;getNames();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the page is initialized, the function &amp;lt;code&amp;gt;getNames()&amp;lt;/code&amp;gt; is called, which obtains the &amp;lt;code&amp;gt;Names&amp;lt;/code&amp;gt; array from the ODB and stores it into an array. After the names have been received, the function calls the usual &amp;lt;code&amp;gt;mhttpd_init()&amp;lt;/code&amp;gt; function. The &amp;quot;modbvalue&amp;quot; path is now set to &amp;lt;code&amp;gt;data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;lt;/code&amp;gt;. This function does a lookup in the &amp;lt;code&amp;gt;names&amp;lt;/code&amp;gt; array and returns the proper index, which is then used to access the corresponding value in the variables array. This method has the advantage that if the indices of an array change (e.g. by adding new variables at the front), the code does not have to update any index since the indices are calculated dynamically.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Implementing SVGs =&lt;br /&gt;
&lt;br /&gt;
An SVG file loaded on a custom page can be interactively modified using JavaScript.&lt;br /&gt;
This is useful for graphically displaying ODB values in complex service or detector systems.&lt;br /&gt;
&lt;br /&gt;
An SVG file can be embedded like this:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
&amp;lt;object id=&amp;quot;svgObject&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; data=&amp;quot;Midas_helium_example.svg&amp;quot;&amp;gt;&amp;lt;/object&amp;gt; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Objects within the SVG can be modified—for example, by changing their fill color as shown below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
const svgDoc = document.getElementById(&#039;svgObject&#039;).contentDocument; &lt;br /&gt;
const obj_Valve = svgDoc.getElementById(valveID); &lt;br /&gt;
obj_Valve.style.fill = &amp;quot;#00FF00&amp;quot;; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SVG images are stored in XML format. In Inkscape, object properties can be interactively edited using the XML Editor.&lt;br /&gt;
A full example is given below in [[SVGs on Custom Pages]]&lt;br /&gt;
&lt;br /&gt;
= Tabbed Pages =&lt;br /&gt;
&lt;br /&gt;
Once custom pages get complex, it is possible to distribute the controls over several&lt;br /&gt;
pages. The user can then click on individual tabs at the top to switch between the pages.&lt;br /&gt;
This can be implemented following this pattern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mtabs&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab active&amp;quot; data-target=&amp;quot;p1&amp;quot;&amp;gt;First&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab&amp;quot; data-target=&amp;quot;p2&amp;quot;&amp;gt;Second&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab&amp;quot; data-target=&amp;quot;p3&amp;quot;&amp;gt;Third&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p1&amp;quot; class=&amp;quot;mpanel active&amp;quot;&amp;gt;&lt;br /&gt;
   Page 1&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p2&amp;quot; class=&amp;quot;mpanel&amp;quot;&amp;gt;&lt;br /&gt;
   Page 2&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p3&amp;quot; class=&amp;quot;mpanel&amp;quot;&amp;gt;&lt;br /&gt;
   Page 3&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates three pages with three associated tabs &amp;quot;First&amp;quot;, &amp;quot;Second&amp;quot; and &amp;quot;Third&amp;quot;. The &amp;quot;active&amp;quot; flag indicates which tab is active initially. To activate a tab programmatically, one can simulate a mouse click on that tab with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
let tab = document.getElementsByClassName(&#039;mtab&#039;);&lt;br /&gt;
tab[1].click(); // index is 0, 1, 2 for the three pages&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is then what you get:&lt;br /&gt;
&lt;br /&gt;
[[File:Tabbed page.png|frame|left|alt=Example of a tabbed custom page|Example of a tabbed custom page]]&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 1  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3595</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3595"/>
		<updated>2026-01-20T10:55:20Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Tabbed Pages */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.&lt;br /&gt;
&lt;br /&gt;
We note that MIDAS has provided a number of different ways of providing custom pages over the years.  A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a &amp;quot;virtual&amp;quot; X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.&lt;br /&gt;
&lt;br /&gt;
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.&lt;br /&gt;
&lt;br /&gt;
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in&lt;br /&gt;
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: With the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new tab/window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
To enable clicks on a menu item that result in an onclick event (no html refresh), one can create and ODB key in the /Custom (or its subdirectories) whose value is a string that does not end with a valid extension (txt|jpg|jpeg|png|gif|pdf|csv|html|js|css). In this case, the string will be encapsulated into and onclick event. For example,&lt;br /&gt;
odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    Click                             alert(1)&lt;br /&gt;
&lt;br /&gt;
Clicking on the menu item &amp;quot;Click&amp;quot; will result in an alert window.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.&lt;br /&gt;
&lt;br /&gt;
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions.  The advantages of using these modb* javascript functions are&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient.  In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call).  It will also require more coding to implement the custom page with only MjsonRPC calls.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Color of gauge bar, &amp;quot;black&amp;quot; if not present. Alternatively one can define color segments like &#039;&#039;&#039;[{0, &#039;green&#039;},{7,&#039;yellow&#039;},{9,&#039;red}]&#039;&#039;&#039; to change the bar color dynamically. If the value is above 0 and blow 7, the bar will be green in this example, yellow between 7 and below 9, and red at or above 9.&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Dynamic ODB paths ==&lt;br /&gt;
&lt;br /&gt;
The path of a custom control is static, meaning it has to be written directly inside the control via &amp;lt;code&amp;gt;data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;lt;/code&amp;gt;. There can be however cases where the path is only known at runtime. For these cases, the path can be a user function which gets called during each update of the control and has to return the current ODB path. This can be useful for equipment values with names. The standard scheme of MIDAS is to have slow control variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Variables&amp;lt;/code&amp;gt; and the names of these variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names&amp;lt;/code&amp;gt;. If there are several different arrays under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;, you might also have something like &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names XXX&amp;lt;/code&amp;gt; where &amp;lt;code&amp;gt;XXX&amp;lt;/code&amp;gt; is the name of the key under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;. If one wants to use a name of a variable rather than its index, the dynamic path function can be uses like in the following example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
   let names;&lt;br /&gt;
&lt;br /&gt;
   function getNames() {&lt;br /&gt;
      mjsonrpc_db_get_value(&amp;quot;/Equipment/.../Settings/Names&amp;quot;).then(function (rpc) {&lt;br /&gt;
         names = rpc.result.data[0];&lt;br /&gt;
         mhttpd_init(&#039;Custom Page Example&#039;);&lt;br /&gt;
      });&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   function getpath(p) {&lt;br /&gt;
      if (mscbNames)&lt;br /&gt;
         return `/Equipment/.../Variables/MSCB[${names.indexOf(p)}]`;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;getNames();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the page is initialized, the function &amp;lt;code&amp;gt;getNames()&amp;lt;/code&amp;gt; is called, which obtains the &amp;lt;code&amp;gt;Names&amp;lt;/code&amp;gt; array from the ODB and stores it into an array. After the names have been received, the function calls the usual &amp;lt;code&amp;gt;mhttpd_init()&amp;lt;/code&amp;gt; function. The &amp;quot;modbvalue&amp;quot; path is now set to &amp;lt;code&amp;gt;data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;lt;/code&amp;gt;. This function does a lookup in the &amp;lt;code&amp;gt;names&amp;lt;/code&amp;gt; array and returns the proper index, which is then used to access the corresponding value in the variables array. This method has the advantage that if the indices of an array change (e.g. by adding new variables at the front), the code does not have to update any index since the indices are calculated dynamically.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Implementing SVGs =&lt;br /&gt;
&lt;br /&gt;
An SVG file loaded on a custom page can be interactively modified using JavaScript.&lt;br /&gt;
This is useful for graphically displaying ODB values in complex service or detector systems.&lt;br /&gt;
&lt;br /&gt;
An SVG file can be embedded like this:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
&amp;lt;object id=&amp;quot;svgObject&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; data=&amp;quot;Midas_helium_example.svg&amp;quot;&amp;gt;&amp;lt;/object&amp;gt; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Objects within the SVG can be modified—for example, by changing their fill color as shown below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
const svgDoc = document.getElementById(&#039;svgObject&#039;).contentDocument; &lt;br /&gt;
const obj_Valve = svgDoc.getElementById(valveID); &lt;br /&gt;
obj_Valve.style.fill = &amp;quot;#00FF00&amp;quot;; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SVG images are stored in XML format. In Inkscape, object properties can be interactively edited using the XML Editor.&lt;br /&gt;
A full example is given below in [[SVGs on Custom Pages]]&lt;br /&gt;
&lt;br /&gt;
= Tabbed Pages =&lt;br /&gt;
&lt;br /&gt;
Once custom pages get complex, it is possible to distribute the controls over several&lt;br /&gt;
pages. The user can then click on individual tabs at the top to switch between the pages.&lt;br /&gt;
This can be implemented following this pattern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mtabs&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab active&amp;quot; data-target=&amp;quot;p1&amp;quot;&amp;gt;First&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab&amp;quot; data-target=&amp;quot;p2&amp;quot;&amp;gt;Second&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab&amp;quot; data-target=&amp;quot;p3&amp;quot;&amp;gt;Third&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p1&amp;quot; class=&amp;quot;mpanel active&amp;quot;&amp;gt;&lt;br /&gt;
   Page 1&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p2&amp;quot; class=&amp;quot;mpanel&amp;quot;&amp;gt;&lt;br /&gt;
   Page 2&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p3&amp;quot; class=&amp;quot;mpanel&amp;quot;&amp;gt;&lt;br /&gt;
   Page 3&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates three pages with three associated tabs &amp;quot;First&amp;quot;, &amp;quot;Second&amp;quot; and &amp;quot;Third&amp;quot;. The &amp;quot;active&amp;quot; flag indicates which tab is active initially. To activate a tab programmatically, one can simulate a mouse click on that tab with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
let tab = document.getElementsByClassName(&#039;mtab&#039;);&lt;br /&gt;
tab[1].click(); // index is 0, 1, 2 for the three pages&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is then what you get:&lt;br /&gt;
&lt;br /&gt;
[[File:Tabbed page.png|left|alt=Example of a tabbed custom page|Example of a tabbed custom page]]&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 1  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3594</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3594"/>
		<updated>2026-01-20T10:55:02Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: Added tabbed pages&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.&lt;br /&gt;
&lt;br /&gt;
We note that MIDAS has provided a number of different ways of providing custom pages over the years.  A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a &amp;quot;virtual&amp;quot; X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.&lt;br /&gt;
&lt;br /&gt;
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.&lt;br /&gt;
&lt;br /&gt;
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in&lt;br /&gt;
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: With the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new tab/window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
To enable clicks on a menu item that result in an onclick event (no html refresh), one can create and ODB key in the /Custom (or its subdirectories) whose value is a string that does not end with a valid extension (txt|jpg|jpeg|png|gif|pdf|csv|html|js|css). In this case, the string will be encapsulated into and onclick event. For example,&lt;br /&gt;
odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    Click                             alert(1)&lt;br /&gt;
&lt;br /&gt;
Clicking on the menu item &amp;quot;Click&amp;quot; will result in an alert window.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.&lt;br /&gt;
&lt;br /&gt;
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions.  The advantages of using these modb* javascript functions are&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient.  In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call).  It will also require more coding to implement the custom page with only MjsonRPC calls.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Color of gauge bar, &amp;quot;black&amp;quot; if not present. Alternatively one can define color segments like &#039;&#039;&#039;[{0, &#039;green&#039;},{7,&#039;yellow&#039;},{9,&#039;red}]&#039;&#039;&#039; to change the bar color dynamically. If the value is above 0 and blow 7, the bar will be green in this example, yellow between 7 and below 9, and red at or above 9.&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Dynamic ODB paths ==&lt;br /&gt;
&lt;br /&gt;
The path of a custom control is static, meaning it has to be written directly inside the control via &amp;lt;code&amp;gt;data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;lt;/code&amp;gt;. There can be however cases where the path is only known at runtime. For these cases, the path can be a user function which gets called during each update of the control and has to return the current ODB path. This can be useful for equipment values with names. The standard scheme of MIDAS is to have slow control variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Variables&amp;lt;/code&amp;gt; and the names of these variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names&amp;lt;/code&amp;gt;. If there are several different arrays under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;, you might also have something like &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names XXX&amp;lt;/code&amp;gt; where &amp;lt;code&amp;gt;XXX&amp;lt;/code&amp;gt; is the name of the key under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;. If one wants to use a name of a variable rather than its index, the dynamic path function can be uses like in the following example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
   let names;&lt;br /&gt;
&lt;br /&gt;
   function getNames() {&lt;br /&gt;
      mjsonrpc_db_get_value(&amp;quot;/Equipment/.../Settings/Names&amp;quot;).then(function (rpc) {&lt;br /&gt;
         names = rpc.result.data[0];&lt;br /&gt;
         mhttpd_init(&#039;Custom Page Example&#039;);&lt;br /&gt;
      });&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   function getpath(p) {&lt;br /&gt;
      if (mscbNames)&lt;br /&gt;
         return `/Equipment/.../Variables/MSCB[${names.indexOf(p)}]`;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;getNames();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the page is initialized, the function &amp;lt;code&amp;gt;getNames()&amp;lt;/code&amp;gt; is called, which obtains the &amp;lt;code&amp;gt;Names&amp;lt;/code&amp;gt; array from the ODB and stores it into an array. After the names have been received, the function calls the usual &amp;lt;code&amp;gt;mhttpd_init()&amp;lt;/code&amp;gt; function. The &amp;quot;modbvalue&amp;quot; path is now set to &amp;lt;code&amp;gt;data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;lt;/code&amp;gt;. This function does a lookup in the &amp;lt;code&amp;gt;names&amp;lt;/code&amp;gt; array and returns the proper index, which is then used to access the corresponding value in the variables array. This method has the advantage that if the indices of an array change (e.g. by adding new variables at the front), the code does not have to update any index since the indices are calculated dynamically.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Implementing SVGs =&lt;br /&gt;
&lt;br /&gt;
An SVG file loaded on a custom page can be interactively modified using JavaScript.&lt;br /&gt;
This is useful for graphically displaying ODB values in complex service or detector systems.&lt;br /&gt;
&lt;br /&gt;
An SVG file can be embedded like this:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
&amp;lt;object id=&amp;quot;svgObject&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; data=&amp;quot;Midas_helium_example.svg&amp;quot;&amp;gt;&amp;lt;/object&amp;gt; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Objects within the SVG can be modified—for example, by changing their fill color as shown below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
const svgDoc = document.getElementById(&#039;svgObject&#039;).contentDocument; &lt;br /&gt;
const obj_Valve = svgDoc.getElementById(valveID); &lt;br /&gt;
obj_Valve.style.fill = &amp;quot;#00FF00&amp;quot;; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SVG images are stored in XML format. In Inkscape, object properties can be interactively edited using the XML Editor.&lt;br /&gt;
A full example is given below in [[SVGs on Custom Pages]]&lt;br /&gt;
&lt;br /&gt;
= Tabbed Pages =&lt;br /&gt;
&lt;br /&gt;
Once custom pages get complex, it is possible to distribute the controls over several&lt;br /&gt;
pages. The user can then click on individual tabs at the top to switch between the pages.&lt;br /&gt;
This can be implemented following this pattern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mtabs&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab active&amp;quot; data-target=&amp;quot;p1&amp;quot;&amp;gt;First&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab&amp;quot; data-target=&amp;quot;p2&amp;quot;&amp;gt;Second&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mtab&amp;quot; data-target=&amp;quot;p3&amp;quot;&amp;gt;Third&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p1&amp;quot; class=&amp;quot;mpanel active&amp;quot;&amp;gt;&lt;br /&gt;
   Page 1&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p2&amp;quot; class=&amp;quot;mpanel&amp;quot;&amp;gt;&lt;br /&gt;
   Page 2&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;p3&amp;quot; class=&amp;quot;mpanel&amp;quot;&amp;gt;&lt;br /&gt;
   Page 3&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates three pages with three associated tabs &amp;quot;First&amp;quot;, &amp;quot;Second&amp;quot; and &amp;quot;Third&amp;quot;. The &amp;quot;active&amp;quot; flag indicates which tab is active initially. To activate a tab programmatically, one can simulate a mouse click on that tab with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
let tab = document.getElementsByClassName(&#039;mtab&#039;);&lt;br /&gt;
tab[1].click(); // index is 0, 1, 2 for the three pages&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is then what you get:&lt;br /&gt;
&lt;br /&gt;
[[File:Tabbed page.png|center|alt=Example of a tabbed custom page|Example of a tabbed custom page]]&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 1  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=File:Tabbed_page.png&amp;diff=3593</id>
		<title>File:Tabbed page.png</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=File:Tabbed_page.png&amp;diff=3593"/>
		<updated>2026-01-20T10:53:16Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A tabbed page example&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Install_Script&amp;diff=3592</id>
		<title>Install Script</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Install_Script&amp;diff=3592"/>
		<updated>2026-01-19T06:56:46Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* MIDAS installation via script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== MIDAS installation via script ==&lt;br /&gt;
&lt;br /&gt;
To install MIDAS vis the install script, make sure that following packets are installed:&lt;br /&gt;
&lt;br /&gt;
* git&lt;br /&gt;
* cmake&lt;br /&gt;
* g++ / c++&lt;br /&gt;
* zlib1g-dev&lt;br /&gt;
&lt;br /&gt;
Then paste following command in a terminal or shell prompt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/bin/bash -c &amp;quot;$(curl -sS https://bitbucket.org/tmidas/midas/raw/HEAD/install.sh)&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The script will:&lt;br /&gt;
&lt;br /&gt;
* Check if all necessary tools and libraries are installed and break if not&lt;br /&gt;
* Ask for the installation directory, the midas experiment directory and the experiment name&lt;br /&gt;
* Download midas via &#039;&#039;git clone&#039;&#039;&lt;br /&gt;
* Compile midas via &#039;&#039;cmake&#039;&#039;&lt;br /&gt;
* Install midas under the user $MIDASSYS/bin directory where $MIDASSYS is the midas installation directory&lt;br /&gt;
* Add the above directory to the path and put the environment variables MIDASSYS, MIDAS_DIR and MIDAS_EXPT_NAME in your environment&lt;br /&gt;
* Add $MIDASSYS/python to PYTHONPATH&lt;br /&gt;
* Create your experiment directory&lt;br /&gt;
* Load an initial ODB file which configures mhttpd to accept insecure connections through port 8081. Change that later to secure your connections.&lt;br /&gt;
* Install the MIDAS web server &#039;&#039;mhttpd&#039;&#039; as a system daemon via systemctl and start it&lt;br /&gt;
* Start a midas example frontend and the logger&lt;br /&gt;
&lt;br /&gt;
After a successful installation, you should be able to connect to mhttpd via http://localhost:8081 and see your experiment&lt;br /&gt;
&lt;br /&gt;
=== Non-interactive installations ===&lt;br /&gt;
It is also possible to first download the &#039;&#039;&#039;install.sh&#039;&#039;&#039; file using the &#039;&#039;&#039;curl&#039;&#039;&#039; command and then executing it directly with &#039;&#039;&#039;./install.sh&#039;&#039;&#039;. Add&lt;br /&gt;
the flag &amp;quot;-f&amp;quot; to force the installation at the default directories without asking for any user input. This can be useful for automated&lt;br /&gt;
installations such as in a Docker.&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Install_Script&amp;diff=3590</id>
		<title>Install Script</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Install_Script&amp;diff=3590"/>
		<updated>2026-01-14T09:49:11Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* MIDAS installation via script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== MIDAS installation via script ==&lt;br /&gt;
&lt;br /&gt;
Paste following command in a terminal or shell prompt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/bin/bash -c &amp;quot;$(curl -sS https://bitbucket.org/tmidas/midas/raw/HEAD/install.sh)&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The script will:&lt;br /&gt;
&lt;br /&gt;
* Check if all necessary tools and libraries are installed and break if not&lt;br /&gt;
* Ask for the installation directory, the midas experiment directory and the experiment name&lt;br /&gt;
* Download midas via &#039;&#039;git clone&#039;&#039;&lt;br /&gt;
* Compile midas via &#039;&#039;cmake&#039;&#039;&lt;br /&gt;
* Install midas under the user $MIDASSYS/bin directory where $MIDASSYS is the midas installation directory&lt;br /&gt;
* Add the above directory to the path and put the environment variables MIDASSYS, MIDAS_DIR and MIDAS_EXPT_NAME in your environment&lt;br /&gt;
* Add $MIDASSYS/python to PYTHONPATH&lt;br /&gt;
* Create your experiment directory&lt;br /&gt;
* Load an initial ODB file which configures mhttpd to accept insecure connections through port 8081. Change that later to secure your connections.&lt;br /&gt;
* Install the MIDAS web server &#039;&#039;mhttpd&#039;&#039; as a system daemon via systemctl and start it&lt;br /&gt;
* Start a midas example frontend and the logger&lt;br /&gt;
&lt;br /&gt;
After a successful installation, you should be able to connect to mhttpd via http://localhost:8081 and see your experiment&lt;br /&gt;
&lt;br /&gt;
=== Non-interactive installations ===&lt;br /&gt;
It is also possible to first download the &#039;&#039;&#039;install.sh&#039;&#039;&#039; file using the &#039;&#039;&#039;curl&#039;&#039;&#039; command and then executing it directly with &#039;&#039;&#039;./install.sh&#039;&#039;&#039;. Add&lt;br /&gt;
the flag &amp;quot;-f&amp;quot; to force the installation at the default directories without asking for any user input. This can be useful for automated&lt;br /&gt;
installations such as in a Docker.&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Install_Script&amp;diff=3589</id>
		<title>Install Script</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Install_Script&amp;diff=3589"/>
		<updated>2026-01-13T18:39:49Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: Created page with &amp;quot;== MIDAS installation via script ==  Paste following command in a terminal or shell prompt:  &amp;lt;code&amp;gt;/bin/bash -c &amp;quot;$(curl -sS https://bitbucket.org/tmidas/midas/raw/HEAD/install.sh)&amp;quot;&amp;lt;/code&amp;gt; &amp;lt;br /&amp;gt;  The script will:  * Check if all necessary tools and libraries are installed and break if not * Ask for the installation directory, the midas experiment directory and the experiment name * Download midas via &amp;#039;&amp;#039;git clone&amp;#039;&amp;#039; * Compile midas via &amp;#039;&amp;#039;cmake&amp;#039;&amp;#039; * Install midas under the u...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== MIDAS installation via script ==&lt;br /&gt;
&lt;br /&gt;
Paste following command in a terminal or shell prompt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/bin/bash -c &amp;quot;$(curl -sS https://bitbucket.org/tmidas/midas/raw/HEAD/install.sh)&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The script will:&lt;br /&gt;
&lt;br /&gt;
* Check if all necessary tools and libraries are installed and break if not&lt;br /&gt;
* Ask for the installation directory, the midas experiment directory and the experiment name&lt;br /&gt;
* Download midas via &#039;&#039;git clone&#039;&#039;&lt;br /&gt;
* Compile midas via &#039;&#039;cmake&#039;&#039;&lt;br /&gt;
* Install midas under the user $MIDASSYS/bin directory where $MIDASSYS is the midas installation directory&lt;br /&gt;
* Add the above directory to the path and put the environment variables MIDASSYS, MIDAS_DIR and MIDAS_EXPT_NAME in your environment&lt;br /&gt;
* Add $MIDASSYS/python to PYTHONPATH&lt;br /&gt;
* Create your experiment directory&lt;br /&gt;
* Load an initial ODB file which configures mhttpd to accept insecure connections through port 8081. Change that later to secure your connections.&lt;br /&gt;
* Install the MIDAS web server &#039;&#039;mhttpd&#039;&#039; as a system daemon via systemctl and start it&lt;br /&gt;
* Start a midas example frontend and the logger&lt;br /&gt;
&lt;br /&gt;
After a successful installation, you should be able to connect to mhttpd via http://localhost:8081 and see your experiment&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Installation&amp;diff=3588</id>
		<title>Installation</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Installation&amp;diff=3588"/>
		<updated>2026-01-13T18:30:20Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Install MIDAS */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
== Install MIDAS ==&lt;br /&gt;
&lt;br /&gt;
There are three ways of installing MIDAS:&lt;br /&gt;
&lt;br /&gt;
* [[Install Script]]&lt;br /&gt;
* [[Quickstart Linux]]&lt;br /&gt;
* [[Setup_MIDAS_experiment|MIDAS installation instructions for TRIUMF experiments]]&lt;br /&gt;
&lt;br /&gt;
There is a page dealing with common problems:&lt;br /&gt;
&lt;br /&gt;
* [[Installation/Compilation problems|Common Installation Problems]]&lt;br /&gt;
&lt;br /&gt;
For TRIUMF experiments, there are pages dealing with recommended configurations and packages for DAQ machines running Linux:&lt;br /&gt;
&lt;br /&gt;
* RHEL/SL/CentOS Linux: https://daq00.triumf.ca/DaqWiki/index.php/SLinstall&lt;br /&gt;
* Ubuntu Linux: https://daq00.triumf.ca/DaqWiki/index.php/Ubuntu&lt;br /&gt;
&lt;br /&gt;
For MacOS installation, please follow the Linux instructions.&lt;br /&gt;
&lt;br /&gt;
For Windows installation, TBW.&lt;br /&gt;
&lt;br /&gt;
There is also a [[Changelog|Changelog and upgrade guide]] for major releases.&lt;br /&gt;
&lt;br /&gt;
[[Category:Installation]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3587</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3587"/>
		<updated>2026-01-09T15:09:41Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* modbgauge */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.&lt;br /&gt;
&lt;br /&gt;
We note that MIDAS has provided a number of different ways of providing custom pages over the years.  A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a &amp;quot;virtual&amp;quot; X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.&lt;br /&gt;
&lt;br /&gt;
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.&lt;br /&gt;
&lt;br /&gt;
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in&lt;br /&gt;
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: With the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new tab/window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
To enable clicks on a menu item that result in an onclick event (no html refresh), one can create and ODB key in the /Custom (or its subdirectories) whose value is a string that does not end with a valid extension (txt|jpg|jpeg|png|gif|pdf|csv|html|js|css). In this case, the string will be encapsulated into and onclick event. For example,&lt;br /&gt;
odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    Click                             alert(1)&lt;br /&gt;
&lt;br /&gt;
Clicking on the menu item &amp;quot;Click&amp;quot; will result in an alert window.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.&lt;br /&gt;
&lt;br /&gt;
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions.  The advantages of using these modb* javascript functions are&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient.  In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call).  It will also require more coding to implement the custom page with only MjsonRPC calls.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Color of gauge bar, &amp;quot;black&amp;quot; if not present. Alternatively one can define color segments like &#039;&#039;&#039;[{0, &#039;green&#039;},{7,&#039;yellow&#039;},{9,&#039;red}]&#039;&#039;&#039; to change the bar color dynamically. If the value is above 0 and blow 7, the bar will be green in this example, yellow between 7 and below 9, and red at or above 9.&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Dynamic ODB paths ==&lt;br /&gt;
&lt;br /&gt;
The path of a custom control is static, meaning it has to be written directly inside the control via &amp;lt;code&amp;gt;data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;lt;/code&amp;gt;. There can be however cases where the path is only known at runtime. For these cases, the path can be a user function which gets called during each update of the control and has to return the current ODB path. This can be useful for equipment values with names. The standard scheme of MIDAS is to have slow control variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Variables&amp;lt;/code&amp;gt; and the names of these variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names&amp;lt;/code&amp;gt;. If there are several different arrays under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;, you might also have something like &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names XXX&amp;lt;/code&amp;gt; where &amp;lt;code&amp;gt;XXX&amp;lt;/code&amp;gt; is the name of the key under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;. If one wants to use a name of a variable rather than its index, the dynamic path function can be uses like in the following example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
   let names;&lt;br /&gt;
&lt;br /&gt;
   function getNames() {&lt;br /&gt;
      mjsonrpc_db_get_value(&amp;quot;/Equipment/.../Settings/Names&amp;quot;).then(function (rpc) {&lt;br /&gt;
         names = rpc.result.data[0];&lt;br /&gt;
         mhttpd_init(&#039;Custom Page Example&#039;);&lt;br /&gt;
      });&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   function getpath(p) {&lt;br /&gt;
      if (mscbNames)&lt;br /&gt;
         return `/Equipment/.../Variables/MSCB[${names.indexOf(p)}]`;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;getNames();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the page is initialized, the function &amp;lt;code&amp;gt;getNames()&amp;lt;/code&amp;gt; is called, which obtains the &amp;lt;code&amp;gt;Names&amp;lt;/code&amp;gt; array from the ODB and stores it into an array. After the names have been received, the function calls the usual &amp;lt;code&amp;gt;mhttpd_init()&amp;lt;/code&amp;gt; function. The &amp;quot;modbvalue&amp;quot; path is now set to &amp;lt;code&amp;gt;data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;lt;/code&amp;gt;. This function does a lookup in the &amp;lt;code&amp;gt;names&amp;lt;/code&amp;gt; array and returns the proper index, which is then used to access the corresponding value in the variables array. This method has the advantage that if the indices of an array change (e.g. by adding new variables at the front), the code does not have to update any index since the indices are calculated dynamically.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Implementing SVGs =&lt;br /&gt;
&lt;br /&gt;
An SVG file loaded on a custom page can be interactively modified using JavaScript.&lt;br /&gt;
This is useful for graphically displaying ODB values in complex service or detector systems.&lt;br /&gt;
&lt;br /&gt;
An SVG file can be embedded like this:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
&amp;lt;object id=&amp;quot;svgObject&amp;quot; type=&amp;quot;image/svg+xml&amp;quot; data=&amp;quot;Midas_helium_example.svg&amp;quot;&amp;gt;&amp;lt;/object&amp;gt; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Objects within the SVG can be modified—for example, by changing their fill color as shown below:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
const svgDoc = document.getElementById(&#039;svgObject&#039;).contentDocument; &lt;br /&gt;
const obj_Valve = svgDoc.getElementById(valveID); &lt;br /&gt;
obj_Valve.style.fill = &amp;quot;#00FF00&amp;quot;; &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SVG images are stored in XML format. In Inkscape, object properties can be interactively edited using the XML Editor.&lt;br /&gt;
A full example is given below in [[SVGs on Custom Pages]]&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 1  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Equipment_Page&amp;diff=3586</id>
		<title>Equipment Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Equipment_Page&amp;diff=3586"/>
		<updated>2026-01-04T13:22:34Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Configuring the equipment page */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
The purpose of the [[mhttpd]] equipment pages is to allow the user to view any Variables related to an equipment. &lt;br /&gt;
&lt;br /&gt;
= Access the equipment page =&lt;br /&gt;
&lt;br /&gt;
The main [[Status Page]] contains a list of all the equipment in an experiment (with each equipment being defined by a frontend). The name of each equipment on that page is a link to the relevant equipment page.&lt;br /&gt;
&lt;br /&gt;
= Configuring the equipment page =&lt;br /&gt;
&lt;br /&gt;
There are several ODB keys that can be defined in {{Odbpath|path=/Equipment/&amp;lt;equip_name&amp;gt;/}} that affect the behaviour of this webpage. See [[/Equipment_ODB_tree#Settings_subtree|the Equipment ODB tree]] documentation for full details, but in brief:&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Variables/&amp;quot; contains the Variables to show on this page (and to record in the history system).&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/Names&amp;quot; or &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/Names &amp;lt;variable&amp;gt;&amp;quot; contains the name of each element in the Variables arrays (e.g. channel names if recording the current of multiple channels).&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/Unit &amp;lt;variable&amp;gt;&amp;quot; contains the units in which a variable is measured.&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/History buttons&amp;quot; is a boolean causing a button displayed after each variable if the flag is set to &#039;&#039;true&#039;&#039;. Clicking on that button causes a floating history panel appearing with the recent history of that variable.&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/Editable&amp;quot; contains a comma-separated list of variables that can be edited on the Equipment page.&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/Grid display&amp;quot; is a boolean which allows you to switch between linear display and &amp;quot;grid&amp;quot; display.  These options are:&lt;br /&gt;
** Grid display: This is useful if there are “channels” with different attributes. Such as HV channels with current and voltage. In this case, we have several array under “Variables” which “belong” together, such as a voltage array and a current array. In this case one should have /Equpiment/&amp;lt;name&amp;gt;/Settings/Grid display = true, which cases the display like at the top of this page (Output on, Power good, Fault, Current). One also should then add “Names” under settings to have proper labels for each channel.  &lt;br /&gt;
** Non-grid display: Here we have arrays not related to each other. Here the grid display should be off, then you get the display directly above, where all banks are shown below each other.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
All the settings are optional, and must be created by the user if desired.&lt;br /&gt;
&lt;br /&gt;
= Example configuration and display =&lt;br /&gt;
&lt;br /&gt;
Click to enlarge each image...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery widths=&amp;quot;300px&amp;quot; mode=&amp;quot;packed&amp;quot;&amp;gt;&lt;br /&gt;
Equipment_Variables.png|Variables&lt;br /&gt;
Equipment_Settings.png|Settings&lt;br /&gt;
Equipment_Page.png|Resulting display&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Equipment_Page&amp;diff=3585</id>
		<title>Equipment Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Equipment_Page&amp;diff=3585"/>
		<updated>2026-01-04T12:59:04Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Configuring the equipment page */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
The purpose of the [[mhttpd]] equipment pages is to allow the user to view any Variables related to an equipment. &lt;br /&gt;
&lt;br /&gt;
= Access the equipment page =&lt;br /&gt;
&lt;br /&gt;
The main [[Status Page]] contains a list of all the equipment in an experiment (with each equipment being defined by a frontend). The name of each equipment on that page is a link to the relevant equipment page.&lt;br /&gt;
&lt;br /&gt;
= Configuring the equipment page =&lt;br /&gt;
&lt;br /&gt;
There are several ODB keys that can be defined in {{Odbpath|path=/Equipment/&amp;lt;equip_name&amp;gt;/}} that affect the behaviour of this webpage. See [[/Equipment_ODB_tree#Settings_subtree|the Equipment ODB tree]] documentation for full details, but in brief:&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Variables/&amp;quot; contains the Variables to show on this page (and to record in the history system).&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/Names&amp;quot; or &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/Names &amp;lt;variable&amp;gt;&amp;quot; contains the name of each element in the Variables arrays (e.g. channel names if recording the current of multiple channels).&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/Unit &amp;lt;variable&amp;gt;&amp;quot; contains the units in which a variable is measured.&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/History buttons&amp;quot; is a boolean causing a button displayed after each variable. Clicking on that button causes a floating history panel appearing with the recent history of that variable.&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/Editable&amp;quot; contains a comma-separated list of variables that can be edited on the Equipment page.&lt;br /&gt;
* &amp;quot;/Equipment/&amp;lt;equip_name&amp;gt;/Settings/Grid display&amp;quot; is a boolean which allows you to switch between linear display and &amp;quot;grid&amp;quot; display.  These options are:&lt;br /&gt;
** Grid display: This is useful if there are “channels” with different attributes. Such as HV channels with current and voltage. In this case, we have several array under “Variables” which “belong” together, such as a voltage array and a current array. In this case one should have /Equpiment/&amp;lt;name&amp;gt;/Settings/Grid display = true, which cases the display like at the top of this page (Output on, Power good, Fault, Current). One also should then add “Names” under settings to have proper labels for each channel.  &lt;br /&gt;
** Non-grid display: Here we have arrays not related to each other. Here the grid display should be off, then you get the display directly above, where all banks are shown below each other.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
All the settings are optional, and must be created by the user if desired.&lt;br /&gt;
&lt;br /&gt;
= Example configuration and display =&lt;br /&gt;
&lt;br /&gt;
Click to enlarge each image...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery widths=&amp;quot;300px&amp;quot; mode=&amp;quot;packed&amp;quot;&amp;gt;&lt;br /&gt;
Equipment_Variables.png|Variables&lt;br /&gt;
Equipment_Settings.png|Settings&lt;br /&gt;
Equipment_Page.png|Resulting display&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3584</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3584"/>
		<updated>2025-12-21T13:03:01Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-type || All plot types || Type of graph, one of &amp;quot;scatter&amp;quot;, &amp;quot;histogram&amp;quot;, &amp;quot;bar&amp;quot;, &amp;quot;colormap&amp;quot;. Default is &amp;quot;scatter&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram, bar || ODB path to X data for the first graph (relative to data-odb-path). If the ODB array is a array of strings, switch to a category x-axis displaying the string labels&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph. n = [1...16]&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt;-error || scatter || ODB path to X errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt;-error || scatter || ODB path to Y errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-stats || scatter, histogram, colormap || Show statistics on upper right corner. Set &amp;quot;1&amp;quot; for display, &amp;quot;0&amp;quot; for no display. Default is &amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-marker&amp;lt;n&amp;gt;-style || scatter || Marker style for &amp;lt;n&amp;gt;-th graph. One of &amp;quot;none&amp;quot;, &amp;quot;circle&amp;quot;, &amp;quot;square&amp;quot;, &amp;quot;diamond&amp;quot;, &amp;quot;pentagon&amp;quot;, &amp;quot;triangle-up&amp;quot;, &amp;quot;triangle-down&amp;quot;, &amp;quot;triangle-left&amp;quot;, &amp;quot;triangle-right&amp;quot;, &amp;quot;cross&amp;quot;, &amp;quot;plus&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-width || scatter, histogram || Line width in pixel. Zero for no line.&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-style || scatter || Line style. One of &amp;quot;none&amp;quot;, &amp;quot;solid&amp;quot;, &amp;quot;dashed&amp;quot;, &amp;quot;dotted&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-bar-width || bar || Width of the bars 0...1. Default is 0.3. At 1 the bars touch each other.&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-zero-color || scatter || Color used for &amp;quot;empty&amp;quot; bins (value &amp;lt; 0.5). Mostly used for 2D histograms to set color of empty bins to white&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-type || scatter || One of &amp;quot;numeric&amp;quot;, &amp;quot;datetime&amp;quot;, &amp;quot;category&amp;quot;. Default is &amp;quot;numeric&amp;quot;. &amp;quot;datetime&amp;quot; assumes X data in UNIX time format. &amp;quot;category&amp;quot; expects X data as a string array of text labels in the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plots&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3583</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3583"/>
		<updated>2025-12-21T13:00:41Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-type || All plot types || Type of graph, one of &amp;quot;scatter&amp;quot;, &amp;quot;histogram&amp;quot;, &amp;quot;bar&amp;quot;, &amp;quot;colormap&amp;quot;. Default is &amp;quot;scatter&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram, bar || ODB path to X data for the first graph (relative to data-odb-path). If the ODB array is a array of strings, switch to a category plot&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph. n = [1...16]&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt;-error || scatter || ODB path to X errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt;-error || scatter || ODB path to Y errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-stats || scatter, histogram, colormap || Show statistics on upper right corner. Set &amp;quot;1&amp;quot; for display, &amp;quot;0&amp;quot; for no display. Default is &amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-marker&amp;lt;n&amp;gt;-style || scatter || Marker style for &amp;lt;n&amp;gt;-th graph. One of &amp;quot;none&amp;quot;, &amp;quot;circle&amp;quot;, &amp;quot;square&amp;quot;, &amp;quot;diamond&amp;quot;, &amp;quot;pentagon&amp;quot;, &amp;quot;triangle-up&amp;quot;, &amp;quot;triangle-down&amp;quot;, &amp;quot;triangle-left&amp;quot;, &amp;quot;triangle-right&amp;quot;, &amp;quot;cross&amp;quot;, &amp;quot;plus&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-width || scatter, histogram || Line width in pixel. Zero for no line.&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-style || scatter || Line style. One of &amp;quot;none&amp;quot;, &amp;quot;solid&amp;quot;, &amp;quot;dashed&amp;quot;, &amp;quot;dotted&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-bar-width || bar || Width of the bars 0...1. Default is 0.3. At 1 the bars touch each other.&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-zero-color || scatter || Color used for &amp;quot;empty&amp;quot; bins (value &amp;lt; 0.5). Mostly used for 2D histograms to set color of empty bins to white&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-type || scatter || One of &amp;quot;numeric&amp;quot;, &amp;quot;datetime&amp;quot;, &amp;quot;category&amp;quot;. Default is &amp;quot;numeric&amp;quot;. &amp;quot;datetime&amp;quot; assumes X data in UNIX time format. &amp;quot;category&amp;quot; expects X data as a string array of text labels in the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plots&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3582</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3582"/>
		<updated>2025-12-21T12:58:55Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-type || All plot types || Type of graph, one of &amp;quot;scatter&amp;quot;, &amp;quot;histogram&amp;quot;, &amp;quot;category&amp;quot;, &amp;quot;colormap&amp;quot;. Default is &amp;quot;scatter&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram, category || ODB path to X data for the first graph (relative to data-odb-path). If the ODB array is a array of strings, switch to a category plot&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph. n = [1...16]&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt;-error || scatter || ODB path to X errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt;-error || scatter || ODB path to Y errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-stats || scatter, histogram, colormap || Show statistics on upper right corner. Set &amp;quot;1&amp;quot; for display, &amp;quot;0&amp;quot; for no display. Default is &amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-marker&amp;lt;n&amp;gt;-style || scatter || Marker style for &amp;lt;n&amp;gt;-th graph. One of &amp;quot;none&amp;quot;, &amp;quot;circle&amp;quot;, &amp;quot;square&amp;quot;, &amp;quot;diamond&amp;quot;, &amp;quot;pentagon&amp;quot;, &amp;quot;triangle-up&amp;quot;, &amp;quot;triangle-down&amp;quot;, &amp;quot;triangle-left&amp;quot;, &amp;quot;triangle-right&amp;quot;, &amp;quot;cross&amp;quot;, &amp;quot;plus&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-width || scatter, histogram || Line width in pixel. Zero for no line.&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-style || scatter || Line style. One of &amp;quot;none&amp;quot;, &amp;quot;solid&amp;quot;, &amp;quot;dashed&amp;quot;, &amp;quot;dotted&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-bar-width || category || Width of the bars 0...1. Default is 0.3. At 1 the bars touch each other.&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-zero-color || scatter || Color used for &amp;quot;empty&amp;quot; bins (value &amp;lt; 0.5). Mostly used for 2D histograms to set color of empty bins to white&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-type || scatter || One of &amp;quot;numeric&amp;quot;, &amp;quot;datetime&amp;quot;, &amp;quot;category&amp;quot;. Default is &amp;quot;numeric&amp;quot;. &amp;quot;datetime&amp;quot; assumes X data in UNIX time format. &amp;quot;category&amp;quot; expects X data as a string array of text labels in the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plots&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3581</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3581"/>
		<updated>2025-12-16T21:21:56Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* JSON parameter set */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram, category || ODB path to X data for the first graph (relative to data-odb-path). If the ODB array is a array of strings, switch to a category plot&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph. n = [1...16]&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt;-error || scatter || ODB path to X errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt;-error || scatter || ODB path to Y errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-stats || scatter, histogram, colormap || Show statistics on upper right corner. Set &amp;quot;1&amp;quot; for display, &amp;quot;0&amp;quot; for no display. Default is &amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-marker&amp;lt;n&amp;gt;-style || scatter || Marker style for &amp;lt;n&amp;gt;-th graph. One of &amp;quot;none&amp;quot;, &amp;quot;circle&amp;quot;, &amp;quot;square&amp;quot;, &amp;quot;diamond&amp;quot;, &amp;quot;pentagon&amp;quot;, &amp;quot;triangle-up&amp;quot;, &amp;quot;triangle-down&amp;quot;, &amp;quot;triangle-left&amp;quot;, &amp;quot;triangle-right&amp;quot;, &amp;quot;cross&amp;quot;, &amp;quot;plus&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-width || scatter, histogram || Line width in pixel. Zero for no line.&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-style || scatter || Line style. One of &amp;quot;none&amp;quot;, &amp;quot;solid&amp;quot;, &amp;quot;dashed&amp;quot;, &amp;quot;dotted&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-bar-width || category || Width of the bars 0...1. Default is 0.3. At 1 the bars touch each other.&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-zero-color || scatter || Color used for &amp;quot;empty&amp;quot; bins (value &amp;lt; 0.5). Mostly used for 2D histograms to set color of empty bins to white&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-type || scatter || One of &amp;quot;numeric&amp;quot;, &amp;quot;datetime&amp;quot;, &amp;quot;category&amp;quot;. Default is &amp;quot;numeric&amp;quot;. &amp;quot;datetime&amp;quot; assumes X data in UNIX time format. &amp;quot;category&amp;quot; expects X data as a string array of text labels in the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plots&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3580</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3580"/>
		<updated>2025-12-15T13:04:29Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram, category || ODB path to X data for the first graph (relative to data-odb-path). If the ODB array is a array of strings, switch to a category plot&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph. n = [1...16]&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt;-error || scatter || ODB path to X errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt;-error || scatter || ODB path to Y errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-stats || scatter, histogram, colormap || Show statistics on upper right corner. Set &amp;quot;1&amp;quot; for display, &amp;quot;0&amp;quot; for no display. Default is &amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-marker&amp;lt;n&amp;gt;-style || scatter || Marker style for &amp;lt;n&amp;gt;-th graph. One of &amp;quot;none&amp;quot;, &amp;quot;circle&amp;quot;, &amp;quot;square&amp;quot;, &amp;quot;diamond&amp;quot;, &amp;quot;pentagon&amp;quot;, &amp;quot;triangle-up&amp;quot;, &amp;quot;triangle-down&amp;quot;, &amp;quot;triangle-left&amp;quot;, &amp;quot;triangle-right&amp;quot;, &amp;quot;cross&amp;quot;, &amp;quot;plus&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-width || scatter, histogram || Line width in pixel. Zero for no line.&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-style || scatter || Line style. One of &amp;quot;none&amp;quot;, &amp;quot;solid&amp;quot;, &amp;quot;dashed&amp;quot;, &amp;quot;dotted&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-bar-width || category || Width of the bars 0...1. Default is 0.3. At 1 the bars touch each other.&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-zero-color || scatter || Color used for &amp;quot;empty&amp;quot; bins (value &amp;lt; 0.5). Mostly used for 2D histograms to set color of empty bins to white&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-type || scatter || One of &amp;quot;numeric&amp;quot;, &amp;quot;datetime&amp;quot;, &amp;quot;category&amp;quot;. Default is &amp;quot;numeric&amp;quot;. &amp;quot;datetime&amp;quot; assumes X data in UNIX time format. &amp;quot;category&amp;quot; expects X data as a string array of text labels in the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3579</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3579"/>
		<updated>2025-12-15T12:38:55Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram, category || ODB path to X data for the first graph (relative to data-odb-path). If the ODB array is a array of strings, switch to a category plot&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph. n = [1...16]&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt;-error || scatter || ODB path to X errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt;-error || scatter || ODB path to Y errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-stats || scatter, histogram, colormap || Show statistics on upper right corner. Set &amp;quot;1&amp;quot; for display, &amp;quot;0&amp;quot; for no display. Default is &amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-marker&amp;lt;n&amp;gt;-style || scatter || Marker style for &amp;lt;n&amp;gt;-th graph. One of &amp;quot;none&amp;quot;, &amp;quot;circle&amp;quot;, &amp;quot;square&amp;quot;, &amp;quot;diamond&amp;quot;, &amp;quot;pentagon&amp;quot;, &amp;quot;triangle-up&amp;quot;, &amp;quot;triangle-down&amp;quot;, &amp;quot;triangle-left&amp;quot;, &amp;quot;triangle-right&amp;quot;, &amp;quot;cross&amp;quot;, &amp;quot;plus&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-width || scatter, histogram || Line width in pixel. Zero for no line.&lt;br /&gt;
|-&lt;br /&gt;
| data-bar-width || category || Width of the bars 0...1. Default is 0.3. At 1 the bars touch each other.&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-zero-color || scatter || Color used for &amp;quot;empty&amp;quot; bins (value &amp;lt; 0.5). Mostly used for 2D histograms to set color of empty bins to white&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-type || scatter || One of &amp;quot;numeric&amp;quot;, &amp;quot;datetime&amp;quot;, &amp;quot;category&amp;quot;. Default is &amp;quot;numeric&amp;quot;. &amp;quot;datetime&amp;quot; assumes X data in UNIX time format. &amp;quot;category&amp;quot; expects X data as a string array of text labels in the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3578</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3578"/>
		<updated>2025-12-15T11:38:34Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram, category || ODB path to X data for the first graph (relative to data-odb-path). If the ODB array is a array of strings, switch to a category plot&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph. n = [1...16]&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt;-error || scatter || ODB path to X errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt;-error || scatter || ODB path to Y errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-stats || scatter, histogram, colormap || Show statistics on upper right corner. Set &amp;quot;1&amp;quot; for display, &amp;quot;0&amp;quot; for no display. Default is &amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-marker&amp;lt;n&amp;gt;-style || scatter || Marker style for &amp;lt;n&amp;gt;-th graph. One of &amp;quot;none&amp;quot;, &amp;quot;circle&amp;quot;, &amp;quot;square&amp;quot;, &amp;quot;diamond&amp;quot;, &amp;quot;pentagon&amp;quot;, &amp;quot;triangle-up&amp;quot;, &amp;quot;triangle-down&amp;quot;, &amp;quot;triangle-left&amp;quot;, &amp;quot;triangle-right&amp;quot;, &amp;quot;cross&amp;quot;, &amp;quot;plus&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-width || scatter, histogram || Line width in pixel. Zero for no line.&lt;br /&gt;
|-&lt;br /&gt;
| data-bar-width || category || Width of the bars 0...1. Default is 0.3. At 1 the bars touch each other.&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-bgcolor || scatter || Color used for &amp;quot;empty&amp;quot; bins (value &amp;lt; 0.5). Mostly used for 2D histograms to set color of empty bins to white&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-type || scatter || One of &amp;quot;numeric&amp;quot;, &amp;quot;datetime&amp;quot;, &amp;quot;category&amp;quot;. Default is &amp;quot;numeric&amp;quot;. &amp;quot;datetime&amp;quot; assumes X data in UNIX time format. &amp;quot;category&amp;quot; expects X data as a string array of text labels in the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3577</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3577"/>
		<updated>2025-12-13T08:35:20Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram, category || ODB path to X data for the first graph (relative to data-odb-path). If the ODB array is a array of strings, switch to a category plot&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt;-error || scatter || ODB path to X errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt;-error || scatter || ODB path to Y errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-stats || scatter, histogram, colormap || Show statistics on upper right corner. Set &amp;quot;1&amp;quot; for display, &amp;quot;0&amp;quot; for no display. Default is &amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-marker&amp;lt;n&amp;gt;-style || scatter || Marker style for &amp;lt;n&amp;gt;-th graph. One of &amp;quot;none&amp;quot;, &amp;quot;circle&amp;quot;, &amp;quot;square&amp;quot;, &amp;quot;diamond&amp;quot;, &amp;quot;pentagon&amp;quot;, &amp;quot;triangle-up&amp;quot;, &amp;quot;triangle-down&amp;quot;, &amp;quot;triangle-left&amp;quot;, &amp;quot;triangle-right&amp;quot;, &amp;quot;cross&amp;quot;, &amp;quot;plus&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-width || scatter, histogram || Line width in pixel. Zero for no line.&lt;br /&gt;
|-&lt;br /&gt;
| data-bar-width || category || Width of the bars 0...1. Default is 0.3. At 1 the bars touch each other.&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-bgcolor || scatter || Color used for &amp;quot;empty&amp;quot; bins (value &amp;lt; 0.5). Mostly used for 2D histograms to set color of empty bins to white&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-type || scatter || One of &amp;quot;numeric&amp;quot;, &amp;quot;datetime&amp;quot;, &amp;quot;category&amp;quot;. Default is &amp;quot;numeric&amp;quot;. &amp;quot;datetime&amp;quot; assumes X data in UNIX time format. &amp;quot;category&amp;quot; expects X data as a string array of text labels in the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3576</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3576"/>
		<updated>2025-12-04T12:32:54Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram || ODB path to X data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt;-error || scatter || ODB path to X errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt;-error || scatter || ODB path to Y errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-stats || scatter, histogram, colormap || Show statistics on upper right corner. Set &amp;quot;1&amp;quot; for display, &amp;quot;0&amp;quot; for no display. Default is &amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-marker&amp;lt;n&amp;gt;-style || scatter || Marker style for &amp;lt;n&amp;gt;-th graph. One of &amp;quot;none&amp;quot;, &amp;quot;circle&amp;quot;, &amp;quot;square&amp;quot;, &amp;quot;diamond&amp;quot;, &amp;quot;pentagon&amp;quot;, &amp;quot;triangle-up&amp;quot;, &amp;quot;triangle-down&amp;quot;, &amp;quot;triangle-left&amp;quot;, &amp;quot;triangle-right&amp;quot;, &amp;quot;cross&amp;quot;, &amp;quot;plus&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-width || scatter, histogram || Line width in pixel. Zero for no line.&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-bgcolor || scatter || Color used for &amp;quot;empty&amp;quot; bins (value &amp;lt; 0.5). Mostly used for 2D histograms to set color of empty bins to white&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-type || scatter || One of &amp;quot;numeric&amp;quot;, &amp;quot;datetime&amp;quot;, &amp;quot;category&amp;quot;. Default is &amp;quot;numeric&amp;quot;. &amp;quot;datetime&amp;quot; assumes X data in UNIX time format. &amp;quot;category&amp;quot; expects X data as a string array of text labels in the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3565</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3565"/>
		<updated>2025-09-21T17:42:42Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram || ODB path to X data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt;-error || scatter || ODB path to X errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt;-error || scatter || ODB path to Y errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-marker&amp;lt;n&amp;gt;-style || scatter || Marker style for &amp;lt;n&amp;gt;-th graph. One of &amp;quot;none&amp;quot;, &amp;quot;circle&amp;quot;, &amp;quot;square&amp;quot;, &amp;quot;diamond&amp;quot;, &amp;quot;pentagon&amp;quot;, &amp;quot;triangle-up&amp;quot;, &amp;quot;triangle-down&amp;quot;, &amp;quot;triangle-left&amp;quot;, &amp;quot;triangle-right&amp;quot;, &amp;quot;cross&amp;quot;, &amp;quot;plus&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-width || scatter, histogram || Line width in pixel. Zero for no line.&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-bgcolor || scatter || Color used for &amp;quot;empty&amp;quot; bins (value &amp;lt; 0.5). Mostly used for 2D histograms to set color of empty bins to white&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-type || scatter || One of &amp;quot;numeric&amp;quot;, &amp;quot;datetime&amp;quot;, &amp;quot;category&amp;quot;. Default is &amp;quot;numeric&amp;quot;. &amp;quot;datetime&amp;quot; assumes X data in UNIX time format. &amp;quot;category&amp;quot; expects X data as a string array of text labels in the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3564</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3564"/>
		<updated>2025-09-21T17:36:43Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram || ODB path to X data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt;-error || scatter || ODB path to X errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt;-error || scatter || ODB path to Y errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-marker&amp;lt;n&amp;gt;-style || scatter || Marker style for &amp;lt;n&amp;gt;-th graph. One of &amp;quot;none&amp;quot;, &amp;quot;circle&amp;quot;, &amp;quot;square&amp;quot;, &amp;quot;diamond&amp;quot;, &amp;quot;pentagon&amp;quot;, &amp;quot;triangle-up&amp;quot;, &amp;quot;triangle-down&amp;quot;, &amp;quot;triangle-left&amp;quot;, &amp;quot;triangle-right&amp;quot;, &amp;quot;cross&amp;quot;, &amp;quot;plus&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-width || scatter, histogram || Line width in pixel. Zero for no line.&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-bgcolor || scatter || Color used for &amp;quot;empty&amp;quot; bins (value &amp;lt; 0.5). Mostly used for 2D histograms to set color of empty bins to white&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-xaxis-type || scatter || One of &amp;quot;numeric&amp;quot;, &amp;quot;datetime&amp;quot;, &amp;quot;category&amp;quot;. Default is &amp;quot;numeric&amp;quot;. &amp;quot;datetime&amp;quot; assumes X data in UNIX time format. &amp;quot;category&amp;quot; expects X data as a string array of text labels in the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3563</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3563"/>
		<updated>2025-09-21T17:21:20Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram || ODB path to X data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt;-error || scatter || ODB path to X errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt;-error || scatter || ODB path to Y errors for the &amp;lt;n&amp;gt;-th graph&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram&lt;br /&gt;
|-&lt;br /&gt;
| data-marker&amp;lt;n&amp;gt;-style || scatter || Marker style for &amp;lt;n&amp;gt;-th graph. One of &amp;quot;none&amp;quot;, &amp;quot;circle&amp;quot;, &amp;quot;square&amp;quot;, &amp;quot;diamond&amp;quot;, &amp;quot;pentagon&amp;quot;, &amp;quot;triangle-up&amp;quot;, &amp;quot;triangle-down&amp;quot;, &amp;quot;triangle-left&amp;quot;, &amp;quot;triangle-right&amp;quot;, &amp;quot;cross&amp;quot;, &amp;quot;plus&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-width || scatter, histogram || Line width in pixel. Zero for no line.&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-bgcolor || scatter || Color used for &amp;quot;empty&amp;quot; bins (value &amp;lt; 0.5). Mostly used for 2D histograms to set color of empty bins to white&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3562</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3562"/>
		<updated>2025-09-21T17:16:08Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram || ODB path to X data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph if several graphs are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram if several histograms are present (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram if several graphs or histograms are present&lt;br /&gt;
|-&lt;br /&gt;
| data-marker&amp;lt;n&amp;gt;-style || scatter || Marker style for &amp;lt;n&amp;gt;-th graph. One of &amp;quot;none&amp;quot;, &amp;quot;circle&amp;quot;, &amp;quot;square&amp;quot;, &amp;quot;diamond&amp;quot;, &amp;quot;pentagon&amp;quot;, &amp;quot;triangle-up&amp;quot;, &amp;quot;triangle-down&amp;quot;, &amp;quot;triangle-left&amp;quot;, &amp;quot;triangle-right&amp;quot;, &amp;quot;cross&amp;quot;, &amp;quot;plus&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-line&amp;lt;n&amp;gt;-width || scatter, histogram || Line width in pixel. Zero for no line.&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-bgcolor || scatter || Color used for &amp;quot;empty&amp;quot; bins (value &amp;lt; 0.5). Mostly used for 2D histograms to set color of empty bins to white&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3559</id>
		<title>Odbxx</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3559"/>
		<updated>2025-09-09T08:16:38Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Accessing ODB in a buffer of vile (aka &amp;quot;Virtual ODB&amp;quot;) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a &amp;quot;magic&amp;quot; map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.&lt;br /&gt;
&lt;br /&gt;
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/examples/odbxx/odbxx_test.cxx odbxx_test.cxx].&lt;br /&gt;
&lt;br /&gt;
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]]  page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.&lt;br /&gt;
&lt;br /&gt;
== Basic usage ==&lt;br /&gt;
&lt;br /&gt;
The simplest usage is like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Grab a single value from the ODB&lt;br /&gt;
int r = midas::odb(&amp;quot;/Runinfo/Run number&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Grab a subtree from the ODB&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Simple read using the subtree, causes a re-read of the ODB value in the background&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The current transition timeout is &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&lt;br /&gt;
// Make a change. The new value is automatically sent to the ODB.&lt;br /&gt;
// Most C++ operators are supported (++, += etc), or you can do a simple&lt;br /&gt;
// re-assignment like `exp[&amp;quot;Transition timeout&amp;quot;] = 12345;`.&lt;br /&gt;
exp[&amp;quot;Transition timeout&amp;quot;] += 100;&lt;br /&gt;
&lt;br /&gt;
// Read the new value&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The transition timeout is now &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that keys in the ODB are case insensitive, thus &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; will give you the same key as &amp;lt;code&amp;gt;exp[&amp;quot;Transition TIMEOUT&amp;quot;]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;int curr_timeout = exp[&amp;quot;Transition timeout&amp;quot;];&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The ODB directory you connect to (&amp;quot;/Experiment&amp;quot; in the above example), has to start with a &amp;quot;/&amp;quot; to tell the midas::odb object&lt;br /&gt;
to connect directly to the ODB. Otherwise, a simple local midas::odb string object gets created without any connection to the ODB.&lt;br /&gt;
&lt;br /&gt;
== Automatic refreshing ==&lt;br /&gt;
&lt;br /&gt;
You may temporarily disable the automatic updating to/from the ODB&lt;br /&gt;
using &amp;lt;code&amp;gt;odb::set_auto_refresh_write(false)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;odb::set_auto_refresh_read(false)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If auto-refresh is enabled (the default), your new values are sent to&lt;br /&gt;
the ODB as soon as you touch the value in the &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object. The ODB&lt;br /&gt;
is queried for new values whenever you access the value. In the above&lt;br /&gt;
example, the ODB is queried 4 times (during construction of &amp;lt;code&amp;gt;exp&amp;lt;/code&amp;gt;, and&lt;br /&gt;
each time &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is mentioned), and written to 1&lt;br /&gt;
time (when &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is assigned to).&lt;br /&gt;
&lt;br /&gt;
See the [[#Callback functions]] section below for details on how to have a function&lt;br /&gt;
called when a value changes.&lt;br /&gt;
&lt;br /&gt;
== Arrays/vectors ==&lt;br /&gt;
&lt;br /&gt;
ODB arrays are represented by std vectors.&lt;br /&gt;
&lt;br /&gt;
You can access/edit individual elements using []:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;][1] = 1.2;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can completely re-assign content using a std::vector or std::array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(10);&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can resize arrays using &amp;lt;code&amp;gt;odb::resize()&amp;lt;/code&amp;gt;. If the existing array is longer,&lt;br /&gt;
it will be truncated; if shorter it will be extended with default values&lt;br /&gt;
(0 or an empty string).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;].resize(5); // Now is 5 elements long&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that arithmetic operators are supported for arrays, and will apply&lt;br /&gt;
the operation to ALL ELEMENTS IN THE ARRAY:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Create the vector&lt;br /&gt;
std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(2);&lt;br /&gt;
vec[0] = 3;&lt;br /&gt;
vec[1] = 5;&lt;br /&gt;
&lt;br /&gt;
// Assign in ODB&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&lt;br /&gt;
// Multiply ALL elements by 2&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] *= 2;&lt;br /&gt;
&lt;br /&gt;
// odb[&amp;quot;Example&amp;quot;] now contains {6, 10}.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can directly iterate over arrays/vectors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
for (auto it = o[&amp;quot;Int Array&amp;quot;].begin(); it != o[&amp;quot;Int Array&amp;quot;].end(); it++) {&lt;br /&gt;
   int val = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (int val : o[&amp;quot;Int Array&amp;quot;]) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Strings ==&lt;br /&gt;
&lt;br /&gt;
Strings in the ODB are returned as std::string (unlike the midas.h &amp;lt;code&amp;gt;db_get_value()&amp;lt;/code&amp;gt;&lt;br /&gt;
family of functions, where strings are returned as char*). You may have vectors of strings.&lt;br /&gt;
&lt;br /&gt;
== Creating new bits of the ODB ==&lt;br /&gt;
&lt;br /&gt;
You can automatically create bits of the ODB by passing a struct to the&lt;br /&gt;
&amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; constructor, then calling &amp;lt;code&amp;gt;odb::connect()&amp;lt;/code&amp;gt;, like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Define the ODB structure&lt;br /&gt;
midas::odb o = {&lt;br /&gt;
   {&amp;quot;Int32 Key&amp;quot;, 42},&lt;br /&gt;
   {&amp;quot;Bool Key&amp;quot;, true},&lt;br /&gt;
   {&amp;quot;Subdir&amp;quot;, {&lt;br /&gt;
      {&amp;quot;Float key&amp;quot;, 1.2f},     // floats must be explicitly specified&lt;br /&gt;
   }},&lt;br /&gt;
   {&amp;quot;Int Array&amp;quot;, {1, 2, 3}},&lt;br /&gt;
   {&amp;quot;Double Array&amp;quot;, {1.2, 2.3, 3.4}},&lt;br /&gt;
   {&amp;quot;String Array&amp;quot;, {&amp;quot;Hello1&amp;quot;, &amp;quot;Hello2&amp;quot;, &amp;quot;Hello3&amp;quot;}},&lt;br /&gt;
   {&amp;quot;Large Array&amp;quot;, std::array&amp;lt;int, 10&amp;gt;{} },   // array with explicit size&lt;br /&gt;
   {&amp;quot;Large String&amp;quot;, std::string(63, &#039;\0&#039;) },  // string with explicit size&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Then sync the structure. This function&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// If you make the `write_defaults` argument true, then the function&lt;br /&gt;
// - overwrites the value of any keys that are in the ODB with the value in your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
// - deletes any keys that are in the ODB but not your code&lt;br /&gt;
// - updates the order of keys in the ODB to match your code&lt;br /&gt;
o.connect_and_fix_structure(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the ODB path in teh odb::connect() call must start with a &#039;/&#039;. The ODB root path like &amp;lt;code&amp;gt;o.connect(&amp;quot;/&amp;quot;);&amp;lt;/code&amp;gt; is not allowed in this call.&lt;br /&gt;
&lt;br /&gt;
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
existing_key[&amp;quot;MyNewSubKey&amp;quot;] = 1.23;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also create new keys by providing a default value when reading a value. If the key doesn&#039;t already exist, the default value will be used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
double val = existing_key[&amp;quot;MyNewSubKey&amp;quot;](1.23);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Iterating over subkeys ==&lt;br /&gt;
&lt;br /&gt;
You can iterate over subkeys using normal iterator functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
for (auto it = exp.begin(); it != exp.end(); it++) {&lt;br /&gt;
   midas::odb&amp;amp; subkey = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (midas::odb&amp;amp; subkey : exp) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can check whether a subkey exists using &amp;lt;code&amp;gt;odb::is_subkey()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Callback functions ==&lt;br /&gt;
&lt;br /&gt;
You may also set up callback functions that are called whenever a value&lt;br /&gt;
changes, using the &amp;lt;code&amp;gt;odb::watch()&amp;lt;/code&amp;gt; function. Note that you must call&lt;br /&gt;
&amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; (from midas.h) periodically for this to work - deep down it&lt;br /&gt;
is &amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; itself that calls your callback function.&lt;br /&gt;
&lt;br /&gt;
The callback functions can either be a &amp;quot;normal&amp;quot; function, a C++ lambda, or a member function of a C++ class.&lt;br /&gt;
In all cases it should accept one argument - a &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object (passed&lt;br /&gt;
by reference) that contains the new state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a lambda:&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch([](midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a &amp;quot;normal&amp;quot; function:&lt;br /&gt;
void my_function(midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch(my_function);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a member function of a class:&lt;br /&gt;
#include &amp;lt;functional&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void MyClass::my_function(midas::odb&amp;amp; arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MyClass::some_initialisation_code() {&lt;br /&gt;
  // Arguments to std::bind() are: &amp;quot;member function to call&amp;quot;, &amp;quot;object to call that function on&amp;quot;, &amp;quot;placeholder for midas::odb arg&amp;quot;&lt;br /&gt;
  std::function&amp;lt;void(midas::odb&amp;amp;)&amp;gt; callback = std::bind(&amp;amp;MyClass::my_function, this, std::placeholders::_1);&lt;br /&gt;
&lt;br /&gt;
  midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
  to_watch.watch(callback);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Accessing ODB in a buffer of file (aka &amp;quot;Virtual ODB&amp;quot;) ==&lt;br /&gt;
&lt;br /&gt;
You might want to have code using the odbxx API which both works in online environments connecting to the ODB as well as&lt;br /&gt;
in offline environment where you want to access an ODB image in a file or a buffer, but using the same odbxx API calls. You&lt;br /&gt;
can do that by using &amp;lt;code&amp;gt;midas::odb::set_source(source, string);&amp;lt;/code&amp;gt;, where source is either &amp;lt;code&amp;gt;midas::odb::FILE&amp;lt;/code&amp;gt;&lt;br /&gt;
followed by the filename or &amp;lt;code&amp;gt;midas::odb::STRING&amp;lt;/code&amp;gt; followed by a std::string containing the ODB dump. The following API&lt;br /&gt;
calls are exactly the same, except that any write operation is blocked, only reading is possible. This can be handy when writing&lt;br /&gt;
an offline-analyzer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   midas::odb::set_odb_source(midas::odb::FILE, &amp;quot;odb.json&amp;quot;);&lt;br /&gt;
   midas::odb o(&amp;quot;/Expriment&amp;quot;);&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; o &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you do want to distinguish in your code online from offline access, you can use &amp;lt;code&amp;gt;midas::odb::is_connected_odb()&amp;lt;/code&amp;gt;&lt;br /&gt;
which only return true in an online environment.&lt;br /&gt;
&lt;br /&gt;
== Utility functions ==&lt;br /&gt;
&lt;br /&gt;
There are various utility functions which can be used:&lt;br /&gt;
&lt;br /&gt;
==== void odb::create(const char *name, int type) ====&lt;br /&gt;
&lt;br /&gt;
Simple wrapper around db_create_key() to create a single key in the ODB. &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; is one of TID_xxx.&lt;br /&gt;
&lt;br /&gt;
==== void odb::delete_key() ====&lt;br /&gt;
&lt;br /&gt;
This member function of a midas::odb object deletes that object from the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb o(&amp;quot;/Some/ODB/Path&amp;quot;);&lt;br /&gt;
o.delete_key();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== int odb::delete_key(const std::string &amp;amp;name) ====&lt;br /&gt;
&lt;br /&gt;
This function deletes a key or a subtree in the ODB passed by its path in the &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.&lt;br /&gt;
&lt;br /&gt;
==== bool odb::exists(const std::string *name) ====&lt;br /&gt;
&lt;br /&gt;
This boolean function checks if a key given by its name exists in the ODB.&lt;br /&gt;
&lt;br /&gt;
==== void odb::load(const std::string &amp;amp;filename, const std::string &amp;amp;odb_path) ====&lt;br /&gt;
&lt;br /&gt;
Load a part (or the whole) ODB from a .JSON or .XML file. Put it under a certain path in the ODB.&lt;br /&gt;
&lt;br /&gt;
==== void odb::save(const std::string &amp;amp;filename) ====&lt;br /&gt;
&lt;br /&gt;
Save a midas::odb object to a file in JSON format. Save the whole ODB with:&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb o(&amp;quot;/&amp;quot;);&lt;br /&gt;
o.save(&amp;quot;odb.json&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====&lt;br /&gt;
&lt;br /&gt;
These functions set and retrieve the debug flag. If the debug flag is &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.&lt;br /&gt;
&lt;br /&gt;
== Example code ==&lt;br /&gt;
&lt;br /&gt;
A full working example exploring most of the features can be found in&lt;br /&gt;
&amp;lt;code&amp;gt;odbxx/odbxx_test.cxx&amp;lt;/code&amp;gt;. The test executable will be compiled as&lt;br /&gt;
&amp;lt;code&amp;gt;build/odbxx/odbxx_test&amp;lt;/code&amp;gt; (it is not installed in the `bin` directory).&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Midas_documentation&amp;diff=3547</id>
		<title>Midas documentation</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Midas_documentation&amp;diff=3547"/>
		<updated>2025-08-29T16:24:30Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Online Database */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; {{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Documentation organization==&lt;br /&gt;
Even after two versions of the Midas documentation, it is still a real challenge to describe the Midas package in a clear, concise and useful way. On this third attempt, we reworked the layout and placed everything on Wiki, hoping that collaborators will be keen to correct, add and improve its content. Feel free to do so by contacting us in case of unclear or incomplete information.&lt;br /&gt;
&lt;br /&gt;
The documentation of the &#039;&#039;&#039;MIDAS code&#039;&#039;&#039; is still generated by Doxygen and can be found at&lt;br /&gt;
[https://daq00.triumf.ca/~daqweb/doc/midas-develop/html/].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We split the documentation in 4 main sections: &#039;&#039;&#039;Installation&#039;&#039;&#039;, &#039;&#039;&#039;Feature listing&#039;&#039;&#039;, &#039;&#039;&#039;Application listing&#039;&#039;&#039;, &#039;&#039;&#039;Online Database&#039;&#039;&#039;.&lt;br /&gt;
While the first one provides you a means to get Midas up and running, the &#039;&#039;&#039;Feature listing&#039;&#039;&#039; should highlight what Midas can do and provide some general information on its implementation.&lt;br /&gt;
The &#039;&#039;&#039;Application listing&#039;&#039;&#039; refers to &#039;system- &amp;amp; user- build&#039; applications that maybe needed during the acquisition. The &#039;&#039;&#039;Online Database&#039;&#039;&#039; section describes in details the Midas central information database. &lt;br /&gt;
Appendices as the last section collect references, and features information assuming that the reader is already familiar with the Midas environment.&lt;br /&gt;
&lt;br /&gt;
A lot of information can be replicated in every section, but we try to minimize it by cross referencing elements between the different sections.&lt;br /&gt;
The hope is that once you have found the feature that you need, the involved application and the activation of that feature with its specific online database section will be easily identifiable.&lt;br /&gt;
&lt;br /&gt;
==[[Installation]]==&lt;br /&gt;
Although originally Midas was available for all sorts of OS, nowadays we tend to reduce its support to Linux-based, MacOS, and Windows.&lt;br /&gt;
This section contains general information and procedure for installation, configuration and examples of basic operation.&lt;br /&gt;
[[Installation|&#039;&#039;&#039;more...&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==[[Feature listing]]==&lt;br /&gt;
Midas provides by default a complete DAQ system, the main features of which comprise: frontend template for acquiring your hardware information, data transfer mechanism to local/remote computer, data logging capability, data analysis framework, data monitoring, full run control, and web interface for experiment control/monitoring.&lt;br /&gt;
While each of these features is described, other potentially useful but &#039;&#039;hidden&#039;&#039; options such as sub-run, messages, history, run sequencer, alarms, event notification, etc,  may also be essential to your experiment. A list of such features with description, enabling method, and operation is also provided. [[Feature_listing|&#039;&#039;&#039;more...&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==[[Application listing]]==&lt;br /&gt;
Midas comes with a set of applications for monitoring and control of the acquisition system such as a central database editor (odbedit), webserver (mhttpd), data logger (mlogger) etc. This section describes each individual task and link to the corresponding features and online database entry managing such a feature. [[Application_listing|&#039;&#039;&#039;more...&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==[[Online Database]]==&lt;br /&gt;
The online database (ODB) is the DAQ central information hub for a given experiment. It contains all information related to the internal operation of the data acquisition and any user information related to the configuration of the experiment. It is accessible by any Midas client application connected to this experiment, and by the user through a command line application (odbedit) or through a web interface (mhttpd).&lt;br /&gt;
&lt;br /&gt;
Its content is organized with multiple directory levels filled with structure, arrays or single variables. While the user can create new directories reflecting his/her hardware or analysis configuration, there are dedicated directories reserved for the core operation of the system (/SYSTEM/, /RUNINFO/, ...) but they still remain readable by the user. [[Online_Database|&#039;&#039;&#039;more...&#039;&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
==MidasGPT==&lt;br /&gt;
A new AI tool based on ChatGPT has been created to be used for asking specific midas questions. Ask [https://chatgpt.com/g/g-68b1cce155a48191bb5df1b2919bdaf4-midasgpt MidasGPT] with questions concerting frontend programs, custom pages and scripting.&lt;br /&gt;
&lt;br /&gt;
==[[Appendices]]==&lt;br /&gt;
* [[Supported Hardware]]&lt;br /&gt;
* [[Mac specific information]]&lt;br /&gt;
* [[Multi-threading option]]&lt;br /&gt;
* [[Event Builder information]]&lt;br /&gt;
* [[MIDAS Event Structure]]&lt;br /&gt;
* [[Common Parameters to MIDAS Utilities]]&lt;br /&gt;
* [[Notes | Miscelleneous notes]] : Notes that need to be incorporated in the documentation.&lt;br /&gt;
* [[Features | New Features]] : documented features to be incorporated in the documentation.&lt;br /&gt;
&lt;br /&gt;
== [[Glossary]] ==&lt;br /&gt;
&lt;br /&gt;
== [https://midas.triumf.ca/MidasWiki/index.php?title=Special%3AAllPages&amp;amp;from=&amp;amp;to=&amp;amp;namespace=0 All Pages listing] ==&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3541</id>
		<title>PySequencer</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=PySequencer&amp;diff=3541"/>
		<updated>2025-07-21T09:47:57Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: Created page with &amp;quot;= Introduction =  The Midas Script Language (MSL) sequencer has been used since many years now, but its syntax is limited. For more demanding tasks requiring complex calculations, a Python-based sequencer has been developed in 2025. The idea is very similar to the MSL sequencer and the user interface is exactly the same, but instead of MSL commands the PySequencer accepts python programs. The interaction with the midas system is done via a special &amp;quot;seq&amp;quot; object. Following...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
&lt;br /&gt;
The Midas Script Language (MSL) sequencer has been used since many years now, but its syntax is limited. For more demanding tasks requiring complex calculations, a Python-based sequencer has been developed in 2025. The idea is very similar to the MSL sequencer and the user interface is exactly the same, but instead of MSL commands the PySequencer accepts python programs. The interaction with the midas system is done via a special &amp;quot;seq&amp;quot; object. Following example illustrates a simple script asking for a number of runs and then starting/stopping these runs, each lasting 60 seconds:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# This is PySequencer test file&lt;br /&gt;
&lt;br /&gt;
def define_params(seq):&lt;br /&gt;
    seq.register_param(&amp;quot;runs&amp;quot;, &amp;quot;Number of runs&amp;quot;, 3)&lt;br /&gt;
&lt;br /&gt;
def sequence(seq):&lt;br /&gt;
    runs = seq.get_param(&amp;quot;runs&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    for r in seq.range(runs):&lt;br /&gt;
        seq.start_run()&lt;br /&gt;
        seq.wait_seconds(60)&lt;br /&gt;
        seq.stop_run()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameters are optional and can be committed. If defined, a dialog box opens at the run of the sequence and queries all parameters.&lt;br /&gt;
&lt;br /&gt;
= Installation =&lt;br /&gt;
&lt;br /&gt;
Before the PySequencer can be used, python must be installed and enabled for midas. See [[Python#Installation | Python Installation]] for details.&lt;br /&gt;
&lt;br /&gt;
Next, the PySequencer menu must be enabled in the ODB with &#039;&#039;&#039;/Experiment/Menu/PySequencer = 1&#039;&#039;&#039;. Now one can click on the &amp;quot;PySequencer&amp;quot; menu item and use the GUI in a similar way than the MSL sequencer. Syntax highlighting happens according to python, and all python variables are shown at the right side of the page for debugging purposes. If the PySequencer is not running, the GUI asks to start &#039;&#039;&#039;python3 $MIDASSYS/python/midas/sequencer.py -D&#039;&#039;&#039;. If this command failed, the user should try to execute it manually to see if there are any errors.&lt;br /&gt;
&lt;br /&gt;
= seq Object Reference =&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;seq.get_param(&#039;&#039;name&#039;&#039;)&#039;&#039;&#039; retrieves a parameter &#039;&#039;name&#039;&#039; from the sequence start&lt;br /&gt;
* &#039;&#039;&#039;seq.start_run&#039;&#039;&#039; starts a run&lt;br /&gt;
* &#039;&#039;&#039;seq.stop_run&#039;&#039;&#039; stops a run&lt;br /&gt;
* &#039;&#039;&#039;seq.wait_seconds(n)&#039;&#039;&#039; waits for &#039;&#039;n&#039;&#039; seconds&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
= Limitations =&lt;br /&gt;
&lt;br /&gt;
Currently numpy cannot be used.&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Feature_listing&amp;diff=3540</id>
		<title>Feature listing</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Feature_listing&amp;diff=3540"/>
		<updated>2025-07-21T09:30:49Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
=== Introduction ===&lt;br /&gt;
Each Feature is described and make reference to the ODB structure and application where needed. The listing is in alphabetical order.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;[[Alarm System]]&#039;&#039;&#039;                           &amp;amp;rarr; &#039;&#039;User and system Alarm&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Data Analysis#Analyzer | Analyzer]]&#039;&#039;&#039;      &amp;amp;rarr; &#039;&#039;Data consumer for Online/Offline data analyzer&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Buffer Manager]]&#039;&#039;&#039;                         &amp;amp;rarr; &#039;&#039;General information&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Data Logger]]&#039;&#039;&#039;                            &amp;amp;rarr; &#039;&#039;Data Consumer for data recording&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Electronic Logbook (ELOG)]]&#039;&#039;&#039;              &amp;amp;rarr; &#039;&#039;Interfacing ELOG to Midas&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Experiment definition]]&#039;&#039;&#039;                  &amp;amp;rarr; &#039;&#039;Several ways of defining and accessing a Midas experiment&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Event Filtering]]&#039;&#039;&#039;                        &amp;amp;rarr; &#039;&#039;Referring to multiple data logging channel&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Frontend Operation]]&#039;&#039;&#039;                     &amp;amp;rarr; &#039;&#039;General information &#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[History System]]&#039;&#039;&#039;                         &amp;amp;rarr; &#039;&#039;Chart history of any of the acquisition variables&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Hot-link]]&#039;&#039;&#039;                               &amp;amp;rarr; &#039;&#039;Event notification mechanism for inter-client communication&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Message System]]&#039;&#039;&#039;                         &amp;amp;rarr; &#039;&#039;General information about message log, info, error reporting&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Online Database|Online Database (ODB)]]&#039;&#039;&#039;  &amp;amp;rarr; &#039;&#039;General information and operation of the central Online Database&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Remote Access to a Midas experiment]]&#039;&#039;&#039;    &amp;amp;rarr; &#039;&#039;Information about connecting to a remote experiment&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Run Control]]&#039;&#039;&#039;                            &amp;amp;rarr; &#039;&#039;General information about run under Midas&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Sequencer|Run Sequencer]]&#039;&#039;&#039;                &amp;amp;rarr; &#039;&#039;Automatic run control&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Lazylogger|Data Archiver]]&#039;&#039;&#039;               &amp;amp;rarr; &#039;&#039;Secondary data logging&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Subruns]]&#039;&#039;&#039;                                &amp;amp;rarr; &#039;&#039;Option for run definition&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Slow Control System]]&#039;&#039;&#039;                    &amp;amp;rarr; &#039;&#039;General information about slow control equipment&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[mhttpd|Web Server]]&#039;&#039;&#039;                      &amp;amp;rarr; &#039;&#039;Midas on the web&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[Python]]&#039;&#039;&#039;                                 &amp;amp;rarr; &#039;&#039;Writing midas clients in python&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;[[PySequencer|Python Sequencer]]&#039;&#039;&#039;           &amp;amp;rarr; &#039;&#039;Automatic run control with python&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Category:Contents]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3539</id>
		<title>Odbxx</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3539"/>
		<updated>2025-07-18T10:07:27Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Accessing ODB in a buffer of vile (aka &amp;quot;Virtual ODB&amp;quot;) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a &amp;quot;magic&amp;quot; map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.&lt;br /&gt;
&lt;br /&gt;
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/examples/odbxx/odbxx_test.cxx odbxx_test.cxx].&lt;br /&gt;
&lt;br /&gt;
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]]  page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.&lt;br /&gt;
&lt;br /&gt;
== Basic usage ==&lt;br /&gt;
&lt;br /&gt;
The simplest usage is like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Grab a single value from the ODB&lt;br /&gt;
int r = midas::odb(&amp;quot;/Runinfo/Run number&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Grab a subtree from the ODB&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Simple read using the subtree, causes a re-read of the ODB value in the background&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The current transition timeout is &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&lt;br /&gt;
// Make a change. The new value is automatically sent to the ODB.&lt;br /&gt;
// Most C++ operators are supported (++, += etc), or you can do a simple&lt;br /&gt;
// re-assignment like `exp[&amp;quot;Transition timeout&amp;quot;] = 12345;`.&lt;br /&gt;
exp[&amp;quot;Transition timeout&amp;quot;] += 100;&lt;br /&gt;
&lt;br /&gt;
// Read the new value&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The transition timeout is now &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that keys in the ODB are case insensitive, thus &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; will give you the same key as &amp;lt;code&amp;gt;exp[&amp;quot;Transition TIMEOUT&amp;quot;]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;int curr_timeout = exp[&amp;quot;Transition timeout&amp;quot;];&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The ODB directory you connect to (&amp;quot;/Experiment&amp;quot; in the above example), has to start with a &amp;quot;/&amp;quot; to tell the midas::odb object&lt;br /&gt;
to connect directly to the ODB. Otherwise, a simple local midas::odb string object gets created without any connection to the ODB.&lt;br /&gt;
&lt;br /&gt;
== Automatic refreshing ==&lt;br /&gt;
&lt;br /&gt;
You may temporarily disable the automatic updating to/from the ODB&lt;br /&gt;
using &amp;lt;code&amp;gt;odb::set_auto_refresh_write(false)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;odb::set_auto_refresh_read(false)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If auto-refresh is enabled (the default), your new values are sent to&lt;br /&gt;
the ODB as soon as you touch the value in the &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object. The ODB&lt;br /&gt;
is queried for new values whenever you access the value. In the above&lt;br /&gt;
example, the ODB is queried 4 times (during construction of &amp;lt;code&amp;gt;exp&amp;lt;/code&amp;gt;, and&lt;br /&gt;
each time &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is mentioned), and written to 1&lt;br /&gt;
time (when &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is assigned to).&lt;br /&gt;
&lt;br /&gt;
See the [[#Callback functions]] section below for details on how to have a function&lt;br /&gt;
called when a value changes.&lt;br /&gt;
&lt;br /&gt;
== Arrays/vectors ==&lt;br /&gt;
&lt;br /&gt;
ODB arrays are represented by std vectors.&lt;br /&gt;
&lt;br /&gt;
You can access/edit individual elements using []:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;][1] = 1.2;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can completely re-assign content using a std::vector or std::array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(10);&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can resize arrays using &amp;lt;code&amp;gt;odb::resize()&amp;lt;/code&amp;gt;. If the existing array is longer,&lt;br /&gt;
it will be truncated; if shorter it will be extended with default values&lt;br /&gt;
(0 or an empty string).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;].resize(5); // Now is 5 elements long&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that arithmetic operators are supported for arrays, and will apply&lt;br /&gt;
the operation to ALL ELEMENTS IN THE ARRAY:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Create the vector&lt;br /&gt;
std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(2);&lt;br /&gt;
vec[0] = 3;&lt;br /&gt;
vec[1] = 5;&lt;br /&gt;
&lt;br /&gt;
// Assign in ODB&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&lt;br /&gt;
// Multiply ALL elements by 2&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] *= 2;&lt;br /&gt;
&lt;br /&gt;
// odb[&amp;quot;Example&amp;quot;] now contains {6, 10}.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can directly iterate over arrays/vectors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
for (auto it = o[&amp;quot;Int Array&amp;quot;].begin(); it != o[&amp;quot;Int Array&amp;quot;].end(); it++) {&lt;br /&gt;
   int val = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (int val : o[&amp;quot;Int Array&amp;quot;]) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Strings ==&lt;br /&gt;
&lt;br /&gt;
Strings in the ODB are returned as std::string (unlike the midas.h &amp;lt;code&amp;gt;db_get_value()&amp;lt;/code&amp;gt;&lt;br /&gt;
family of functions, where strings are returned as char*). You may have vectors of strings.&lt;br /&gt;
&lt;br /&gt;
== Creating new bits of the ODB ==&lt;br /&gt;
&lt;br /&gt;
You can automatically create bits of the ODB by passing a struct to the&lt;br /&gt;
&amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; constructor, then calling &amp;lt;code&amp;gt;odb::connect()&amp;lt;/code&amp;gt;, like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Define the ODB structure&lt;br /&gt;
midas::odb o = {&lt;br /&gt;
   {&amp;quot;Int32 Key&amp;quot;, 42},&lt;br /&gt;
   {&amp;quot;Bool Key&amp;quot;, true},&lt;br /&gt;
   {&amp;quot;Subdir&amp;quot;, {&lt;br /&gt;
      {&amp;quot;Float key&amp;quot;, 1.2f},     // floats must be explicitly specified&lt;br /&gt;
   }},&lt;br /&gt;
   {&amp;quot;Int Array&amp;quot;, {1, 2, 3}},&lt;br /&gt;
   {&amp;quot;Double Array&amp;quot;, {1.2, 2.3, 3.4}},&lt;br /&gt;
   {&amp;quot;String Array&amp;quot;, {&amp;quot;Hello1&amp;quot;, &amp;quot;Hello2&amp;quot;, &amp;quot;Hello3&amp;quot;}},&lt;br /&gt;
   {&amp;quot;Large Array&amp;quot;, std::array&amp;lt;int, 10&amp;gt;{} },   // array with explicit size&lt;br /&gt;
   {&amp;quot;Large String&amp;quot;, std::string(63, &#039;\0&#039;) },  // string with explicit size&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Then sync the structure. This function&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// If you make the `write_defaults` argument true, then the function&lt;br /&gt;
// - overwrites the value of any keys that are in the ODB with the value in your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
// - deletes any keys that are in the ODB but not your code&lt;br /&gt;
// - updates the order of keys in the ODB to match your code&lt;br /&gt;
o.connect_and_fix_structure(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the ODB path in teh odb::connect() call must start with a &#039;/&#039;. The ODB root path like &amp;lt;code&amp;gt;o.connect(&amp;quot;/&amp;quot;);&amp;lt;/code&amp;gt; is not allowed in this call.&lt;br /&gt;
&lt;br /&gt;
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
existing_key[&amp;quot;MyNewSubKey&amp;quot;] = 1.23;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also create new keys by providing a default value when reading a value. If the key doesn&#039;t already exist, the default value will be used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
double val = existing_key[&amp;quot;MyNewSubKey&amp;quot;](1.23);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Iterating over subkeys ==&lt;br /&gt;
&lt;br /&gt;
You can iterate over subkeys using normal iterator functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
for (auto it = exp.begin(); it != exp.end(); it++) {&lt;br /&gt;
   midas::odb&amp;amp; subkey = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (midas::odb&amp;amp; subkey : exp) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can check whether a subkey exists using &amp;lt;code&amp;gt;odb::is_subkey()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Callback functions ==&lt;br /&gt;
&lt;br /&gt;
You may also set up callback functions that are called whenever a value&lt;br /&gt;
changes, using the &amp;lt;code&amp;gt;odb::watch()&amp;lt;/code&amp;gt; function. Note that you must call&lt;br /&gt;
&amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; (from midas.h) periodically for this to work - deep down it&lt;br /&gt;
is &amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; itself that calls your callback function.&lt;br /&gt;
&lt;br /&gt;
The callback functions can either be a &amp;quot;normal&amp;quot; function, a C++ lambda, or a member function of a C++ class.&lt;br /&gt;
In all cases it should accept one argument - a &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object (passed&lt;br /&gt;
by reference) that contains the new state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a lambda:&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch([](midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a &amp;quot;normal&amp;quot; function:&lt;br /&gt;
void my_function(midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch(my_function);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a member function of a class:&lt;br /&gt;
#include &amp;lt;functional&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void MyClass::my_function(midas::odb&amp;amp; arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MyClass::some_initialisation_code() {&lt;br /&gt;
  // Arguments to std::bind() are: &amp;quot;member function to call&amp;quot;, &amp;quot;object to call that function on&amp;quot;, &amp;quot;placeholder for midas::odb arg&amp;quot;&lt;br /&gt;
  std::function&amp;lt;void(midas::odb&amp;amp;)&amp;gt; callback = std::bind(&amp;amp;MyClass::my_function, this, std::placeholders::_1);&lt;br /&gt;
&lt;br /&gt;
  midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
  to_watch.watch(callback);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Accessing ODB in a buffer of vile (aka &amp;quot;Virtual ODB&amp;quot;) ==&lt;br /&gt;
&lt;br /&gt;
You might want to have code using the odbxx API which both works in online environments connecting to the ODB as well as&lt;br /&gt;
in offline environment where you want to access an ODB image in a file or a buffer, but using the same odbxx API calls. You&lt;br /&gt;
can do that by using &amp;lt;code&amp;gt;midas::odb::set_source(source, string);&amp;lt;/code&amp;gt;, where source is either &amp;lt;code&amp;gt;midas::odb::FILE&amp;lt;/code&amp;gt;&lt;br /&gt;
followed by the filename or &amp;lt;code&amp;gt;midas::odb::STRING&amp;lt;/code&amp;gt; followed by a std::string containing the ODB dump. The following API&lt;br /&gt;
calls are exactly the same, except that any write operation is blocked, only reading is possible. This can be handy when writing&lt;br /&gt;
an offline-analyzer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   midas::odb::set_odb_source(midas::odb::FILE, &amp;quot;odb.json&amp;quot;);&lt;br /&gt;
   midas::odb o(&amp;quot;/Expriment&amp;quot;);&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; o &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you do want to distinguish in your code online from offline access, you can use &amp;lt;code&amp;gt;midas::odb::is_connected_odb()&amp;lt;/code&amp;gt;&lt;br /&gt;
which only return true in an online environment.&lt;br /&gt;
&lt;br /&gt;
== Utility functions ==&lt;br /&gt;
&lt;br /&gt;
There are various utility functions which can be used:&lt;br /&gt;
&lt;br /&gt;
==== void odb::create(const char *name, int type) ====&lt;br /&gt;
&lt;br /&gt;
Simple wrapper around db_create_key() to create a single key in the ODB. &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; is one of TID_xxx.&lt;br /&gt;
&lt;br /&gt;
==== void odb::delete_key() ====&lt;br /&gt;
&lt;br /&gt;
This member function of a midas::odb object deletes that object from the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb o(&amp;quot;/Some/ODB/Path&amp;quot;);&lt;br /&gt;
o.delete_key();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== int odb::delete_key(const std::string &amp;amp;name) ====&lt;br /&gt;
&lt;br /&gt;
This function deletes a key or a subtree in the ODB passed by its path in the &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.&lt;br /&gt;
&lt;br /&gt;
==== bool odb::exists(const std::string *name) ====&lt;br /&gt;
&lt;br /&gt;
This boolean function checks if a key given by its name exists in the ODB.&lt;br /&gt;
&lt;br /&gt;
==== void odb::load(const std::string &amp;amp;filename, const std::string &amp;amp;odb_path) ====&lt;br /&gt;
&lt;br /&gt;
Load a part (or the whole) ODB from a .JSON or .XML file. Put it under a certain path in the ODB.&lt;br /&gt;
&lt;br /&gt;
==== void odb::save(const std::string &amp;amp;filename) ====&lt;br /&gt;
&lt;br /&gt;
Save a midas::odb object to a file in JSON format. Save the whole ODB with:&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb o(&amp;quot;/&amp;quot;);&lt;br /&gt;
o.save(&amp;quot;odb.json&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====&lt;br /&gt;
&lt;br /&gt;
These functions set and retrieve the debug flag. If the debug flag is &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.&lt;br /&gt;
&lt;br /&gt;
== Example code ==&lt;br /&gt;
&lt;br /&gt;
A full working example exploring most of the features can be found in&lt;br /&gt;
&amp;lt;code&amp;gt;odbxx/odbxx_test.cxx&amp;lt;/code&amp;gt;. The test executable will be compiled as&lt;br /&gt;
&amp;lt;code&amp;gt;build/odbxx/odbxx_test&amp;lt;/code&amp;gt; (it is not installed in the `bin` directory).&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3538</id>
		<title>Odbxx</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3538"/>
		<updated>2025-07-18T10:04:20Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Callback functions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a &amp;quot;magic&amp;quot; map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.&lt;br /&gt;
&lt;br /&gt;
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/examples/odbxx/odbxx_test.cxx odbxx_test.cxx].&lt;br /&gt;
&lt;br /&gt;
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]]  page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.&lt;br /&gt;
&lt;br /&gt;
== Basic usage ==&lt;br /&gt;
&lt;br /&gt;
The simplest usage is like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Grab a single value from the ODB&lt;br /&gt;
int r = midas::odb(&amp;quot;/Runinfo/Run number&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Grab a subtree from the ODB&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Simple read using the subtree, causes a re-read of the ODB value in the background&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The current transition timeout is &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&lt;br /&gt;
// Make a change. The new value is automatically sent to the ODB.&lt;br /&gt;
// Most C++ operators are supported (++, += etc), or you can do a simple&lt;br /&gt;
// re-assignment like `exp[&amp;quot;Transition timeout&amp;quot;] = 12345;`.&lt;br /&gt;
exp[&amp;quot;Transition timeout&amp;quot;] += 100;&lt;br /&gt;
&lt;br /&gt;
// Read the new value&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The transition timeout is now &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that keys in the ODB are case insensitive, thus &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; will give you the same key as &amp;lt;code&amp;gt;exp[&amp;quot;Transition TIMEOUT&amp;quot;]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;int curr_timeout = exp[&amp;quot;Transition timeout&amp;quot;];&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The ODB directory you connect to (&amp;quot;/Experiment&amp;quot; in the above example), has to start with a &amp;quot;/&amp;quot; to tell the midas::odb object&lt;br /&gt;
to connect directly to the ODB. Otherwise, a simple local midas::odb string object gets created without any connection to the ODB.&lt;br /&gt;
&lt;br /&gt;
== Automatic refreshing ==&lt;br /&gt;
&lt;br /&gt;
You may temporarily disable the automatic updating to/from the ODB&lt;br /&gt;
using &amp;lt;code&amp;gt;odb::set_auto_refresh_write(false)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;odb::set_auto_refresh_read(false)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If auto-refresh is enabled (the default), your new values are sent to&lt;br /&gt;
the ODB as soon as you touch the value in the &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object. The ODB&lt;br /&gt;
is queried for new values whenever you access the value. In the above&lt;br /&gt;
example, the ODB is queried 4 times (during construction of &amp;lt;code&amp;gt;exp&amp;lt;/code&amp;gt;, and&lt;br /&gt;
each time &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is mentioned), and written to 1&lt;br /&gt;
time (when &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is assigned to).&lt;br /&gt;
&lt;br /&gt;
See the [[#Callback functions]] section below for details on how to have a function&lt;br /&gt;
called when a value changes.&lt;br /&gt;
&lt;br /&gt;
== Arrays/vectors ==&lt;br /&gt;
&lt;br /&gt;
ODB arrays are represented by std vectors.&lt;br /&gt;
&lt;br /&gt;
You can access/edit individual elements using []:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;][1] = 1.2;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can completely re-assign content using a std::vector or std::array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(10);&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can resize arrays using &amp;lt;code&amp;gt;odb::resize()&amp;lt;/code&amp;gt;. If the existing array is longer,&lt;br /&gt;
it will be truncated; if shorter it will be extended with default values&lt;br /&gt;
(0 or an empty string).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;].resize(5); // Now is 5 elements long&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that arithmetic operators are supported for arrays, and will apply&lt;br /&gt;
the operation to ALL ELEMENTS IN THE ARRAY:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Create the vector&lt;br /&gt;
std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(2);&lt;br /&gt;
vec[0] = 3;&lt;br /&gt;
vec[1] = 5;&lt;br /&gt;
&lt;br /&gt;
// Assign in ODB&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&lt;br /&gt;
// Multiply ALL elements by 2&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] *= 2;&lt;br /&gt;
&lt;br /&gt;
// odb[&amp;quot;Example&amp;quot;] now contains {6, 10}.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can directly iterate over arrays/vectors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
for (auto it = o[&amp;quot;Int Array&amp;quot;].begin(); it != o[&amp;quot;Int Array&amp;quot;].end(); it++) {&lt;br /&gt;
   int val = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (int val : o[&amp;quot;Int Array&amp;quot;]) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Strings ==&lt;br /&gt;
&lt;br /&gt;
Strings in the ODB are returned as std::string (unlike the midas.h &amp;lt;code&amp;gt;db_get_value()&amp;lt;/code&amp;gt;&lt;br /&gt;
family of functions, where strings are returned as char*). You may have vectors of strings.&lt;br /&gt;
&lt;br /&gt;
== Creating new bits of the ODB ==&lt;br /&gt;
&lt;br /&gt;
You can automatically create bits of the ODB by passing a struct to the&lt;br /&gt;
&amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; constructor, then calling &amp;lt;code&amp;gt;odb::connect()&amp;lt;/code&amp;gt;, like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Define the ODB structure&lt;br /&gt;
midas::odb o = {&lt;br /&gt;
   {&amp;quot;Int32 Key&amp;quot;, 42},&lt;br /&gt;
   {&amp;quot;Bool Key&amp;quot;, true},&lt;br /&gt;
   {&amp;quot;Subdir&amp;quot;, {&lt;br /&gt;
      {&amp;quot;Float key&amp;quot;, 1.2f},     // floats must be explicitly specified&lt;br /&gt;
   }},&lt;br /&gt;
   {&amp;quot;Int Array&amp;quot;, {1, 2, 3}},&lt;br /&gt;
   {&amp;quot;Double Array&amp;quot;, {1.2, 2.3, 3.4}},&lt;br /&gt;
   {&amp;quot;String Array&amp;quot;, {&amp;quot;Hello1&amp;quot;, &amp;quot;Hello2&amp;quot;, &amp;quot;Hello3&amp;quot;}},&lt;br /&gt;
   {&amp;quot;Large Array&amp;quot;, std::array&amp;lt;int, 10&amp;gt;{} },   // array with explicit size&lt;br /&gt;
   {&amp;quot;Large String&amp;quot;, std::string(63, &#039;\0&#039;) },  // string with explicit size&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Then sync the structure. This function&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// If you make the `write_defaults` argument true, then the function&lt;br /&gt;
// - overwrites the value of any keys that are in the ODB with the value in your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
// - deletes any keys that are in the ODB but not your code&lt;br /&gt;
// - updates the order of keys in the ODB to match your code&lt;br /&gt;
o.connect_and_fix_structure(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the ODB path in teh odb::connect() call must start with a &#039;/&#039;. The ODB root path like &amp;lt;code&amp;gt;o.connect(&amp;quot;/&amp;quot;);&amp;lt;/code&amp;gt; is not allowed in this call.&lt;br /&gt;
&lt;br /&gt;
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
existing_key[&amp;quot;MyNewSubKey&amp;quot;] = 1.23;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also create new keys by providing a default value when reading a value. If the key doesn&#039;t already exist, the default value will be used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
double val = existing_key[&amp;quot;MyNewSubKey&amp;quot;](1.23);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Iterating over subkeys ==&lt;br /&gt;
&lt;br /&gt;
You can iterate over subkeys using normal iterator functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
for (auto it = exp.begin(); it != exp.end(); it++) {&lt;br /&gt;
   midas::odb&amp;amp; subkey = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (midas::odb&amp;amp; subkey : exp) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can check whether a subkey exists using &amp;lt;code&amp;gt;odb::is_subkey()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Callback functions ==&lt;br /&gt;
&lt;br /&gt;
You may also set up callback functions that are called whenever a value&lt;br /&gt;
changes, using the &amp;lt;code&amp;gt;odb::watch()&amp;lt;/code&amp;gt; function. Note that you must call&lt;br /&gt;
&amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; (from midas.h) periodically for this to work - deep down it&lt;br /&gt;
is &amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; itself that calls your callback function.&lt;br /&gt;
&lt;br /&gt;
The callback functions can either be a &amp;quot;normal&amp;quot; function, a C++ lambda, or a member function of a C++ class.&lt;br /&gt;
In all cases it should accept one argument - a &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object (passed&lt;br /&gt;
by reference) that contains the new state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a lambda:&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch([](midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a &amp;quot;normal&amp;quot; function:&lt;br /&gt;
void my_function(midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch(my_function);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a member function of a class:&lt;br /&gt;
#include &amp;lt;functional&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void MyClass::my_function(midas::odb&amp;amp; arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MyClass::some_initialisation_code() {&lt;br /&gt;
  // Arguments to std::bind() are: &amp;quot;member function to call&amp;quot;, &amp;quot;object to call that function on&amp;quot;, &amp;quot;placeholder for midas::odb arg&amp;quot;&lt;br /&gt;
  std::function&amp;lt;void(midas::odb&amp;amp;)&amp;gt; callback = std::bind(&amp;amp;MyClass::my_function, this, std::placeholders::_1);&lt;br /&gt;
&lt;br /&gt;
  midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
  to_watch.watch(callback);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Accessing ODB in a buffer of vile (aka &amp;quot;Virtual ODB&amp;quot;) ==&lt;br /&gt;
&lt;br /&gt;
You might want to have code using the odbxx API which both works in online environments connecting to the ODB as well as&lt;br /&gt;
in offline environment where you want to access an ODB image in a file or a buffer, but using the same odbxx API calls. You&lt;br /&gt;
can do that by using &amp;lt;code&amp;gt;midas::odb::set_source(source[, filename]);&amp;lt;/code&amp;gt;, where source is either &amp;lt;code&amp;gt;midas::odb::FILE&amp;lt;/code&amp;gt;&lt;br /&gt;
followed by the filename or &amp;lt;code&amp;gt;midas::odb::STRING&amp;lt;/code&amp;gt; followed by a std::string containing the ODB dump. The following API&lt;br /&gt;
calls are exactly the same, except that any write operation is blocked, only reading is possible. This can be handy when writing&lt;br /&gt;
an offline-analyzer.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   midas::odb::set_odb_source(midas::odb::FILE, &amp;quot;odb.json&amp;quot;);&lt;br /&gt;
   midas::odb o(&amp;quot;/Expriment&amp;quot;);&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; o &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utility functions ==&lt;br /&gt;
&lt;br /&gt;
There are various utility functions which can be used:&lt;br /&gt;
&lt;br /&gt;
==== void odb::create(const char *name, int type) ====&lt;br /&gt;
&lt;br /&gt;
Simple wrapper around db_create_key() to create a single key in the ODB. &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; is one of TID_xxx.&lt;br /&gt;
&lt;br /&gt;
==== void odb::delete_key() ====&lt;br /&gt;
&lt;br /&gt;
This member function of a midas::odb object deletes that object from the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb o(&amp;quot;/Some/ODB/Path&amp;quot;);&lt;br /&gt;
o.delete_key();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== int odb::delete_key(const std::string &amp;amp;name) ====&lt;br /&gt;
&lt;br /&gt;
This function deletes a key or a subtree in the ODB passed by its path in the &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.&lt;br /&gt;
&lt;br /&gt;
==== bool odb::exists(const std::string *name) ====&lt;br /&gt;
&lt;br /&gt;
This boolean function checks if a key given by its name exists in the ODB.&lt;br /&gt;
&lt;br /&gt;
==== void odb::load(const std::string &amp;amp;filename, const std::string &amp;amp;odb_path) ====&lt;br /&gt;
&lt;br /&gt;
Load a part (or the whole) ODB from a .JSON or .XML file. Put it under a certain path in the ODB.&lt;br /&gt;
&lt;br /&gt;
==== void odb::save(const std::string &amp;amp;filename) ====&lt;br /&gt;
&lt;br /&gt;
Save a midas::odb object to a file in JSON format. Save the whole ODB with:&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb o(&amp;quot;/&amp;quot;);&lt;br /&gt;
o.save(&amp;quot;odb.json&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====&lt;br /&gt;
&lt;br /&gt;
These functions set and retrieve the debug flag. If the debug flag is &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.&lt;br /&gt;
&lt;br /&gt;
== Example code ==&lt;br /&gt;
&lt;br /&gt;
A full working example exploring most of the features can be found in&lt;br /&gt;
&amp;lt;code&amp;gt;odbxx/odbxx_test.cxx&amp;lt;/code&amp;gt;. The test executable will be compiled as&lt;br /&gt;
&amp;lt;code&amp;gt;build/odbxx/odbxx_test&amp;lt;/code&amp;gt; (it is not installed in the `bin` directory).&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3537</id>
		<title>Odbxx</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3537"/>
		<updated>2025-07-18T08:58:48Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Utility functions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a &amp;quot;magic&amp;quot; map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.&lt;br /&gt;
&lt;br /&gt;
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/examples/odbxx/odbxx_test.cxx odbxx_test.cxx].&lt;br /&gt;
&lt;br /&gt;
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]]  page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.&lt;br /&gt;
&lt;br /&gt;
== Basic usage ==&lt;br /&gt;
&lt;br /&gt;
The simplest usage is like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Grab a single value from the ODB&lt;br /&gt;
int r = midas::odb(&amp;quot;/Runinfo/Run number&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Grab a subtree from the ODB&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Simple read using the subtree, causes a re-read of the ODB value in the background&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The current transition timeout is &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&lt;br /&gt;
// Make a change. The new value is automatically sent to the ODB.&lt;br /&gt;
// Most C++ operators are supported (++, += etc), or you can do a simple&lt;br /&gt;
// re-assignment like `exp[&amp;quot;Transition timeout&amp;quot;] = 12345;`.&lt;br /&gt;
exp[&amp;quot;Transition timeout&amp;quot;] += 100;&lt;br /&gt;
&lt;br /&gt;
// Read the new value&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The transition timeout is now &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that keys in the ODB are case insensitive, thus &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; will give you the same key as &amp;lt;code&amp;gt;exp[&amp;quot;Transition TIMEOUT&amp;quot;]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;int curr_timeout = exp[&amp;quot;Transition timeout&amp;quot;];&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The ODB directory you connect to (&amp;quot;/Experiment&amp;quot; in the above example), has to start with a &amp;quot;/&amp;quot; to tell the midas::odb object&lt;br /&gt;
to connect directly to the ODB. Otherwise, a simple local midas::odb string object gets created without any connection to the ODB.&lt;br /&gt;
&lt;br /&gt;
== Automatic refreshing ==&lt;br /&gt;
&lt;br /&gt;
You may temporarily disable the automatic updating to/from the ODB&lt;br /&gt;
using &amp;lt;code&amp;gt;odb::set_auto_refresh_write(false)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;odb::set_auto_refresh_read(false)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If auto-refresh is enabled (the default), your new values are sent to&lt;br /&gt;
the ODB as soon as you touch the value in the &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object. The ODB&lt;br /&gt;
is queried for new values whenever you access the value. In the above&lt;br /&gt;
example, the ODB is queried 4 times (during construction of &amp;lt;code&amp;gt;exp&amp;lt;/code&amp;gt;, and&lt;br /&gt;
each time &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is mentioned), and written to 1&lt;br /&gt;
time (when &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is assigned to).&lt;br /&gt;
&lt;br /&gt;
See the [[#Callback functions]] section below for details on how to have a function&lt;br /&gt;
called when a value changes.&lt;br /&gt;
&lt;br /&gt;
== Arrays/vectors ==&lt;br /&gt;
&lt;br /&gt;
ODB arrays are represented by std vectors.&lt;br /&gt;
&lt;br /&gt;
You can access/edit individual elements using []:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;][1] = 1.2;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can completely re-assign content using a std::vector or std::array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(10);&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can resize arrays using &amp;lt;code&amp;gt;odb::resize()&amp;lt;/code&amp;gt;. If the existing array is longer,&lt;br /&gt;
it will be truncated; if shorter it will be extended with default values&lt;br /&gt;
(0 or an empty string).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;].resize(5); // Now is 5 elements long&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that arithmetic operators are supported for arrays, and will apply&lt;br /&gt;
the operation to ALL ELEMENTS IN THE ARRAY:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Create the vector&lt;br /&gt;
std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(2);&lt;br /&gt;
vec[0] = 3;&lt;br /&gt;
vec[1] = 5;&lt;br /&gt;
&lt;br /&gt;
// Assign in ODB&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&lt;br /&gt;
// Multiply ALL elements by 2&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] *= 2;&lt;br /&gt;
&lt;br /&gt;
// odb[&amp;quot;Example&amp;quot;] now contains {6, 10}.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can directly iterate over arrays/vectors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
for (auto it = o[&amp;quot;Int Array&amp;quot;].begin(); it != o[&amp;quot;Int Array&amp;quot;].end(); it++) {&lt;br /&gt;
   int val = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (int val : o[&amp;quot;Int Array&amp;quot;]) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Strings ==&lt;br /&gt;
&lt;br /&gt;
Strings in the ODB are returned as std::string (unlike the midas.h &amp;lt;code&amp;gt;db_get_value()&amp;lt;/code&amp;gt;&lt;br /&gt;
family of functions, where strings are returned as char*). You may have vectors of strings.&lt;br /&gt;
&lt;br /&gt;
== Creating new bits of the ODB ==&lt;br /&gt;
&lt;br /&gt;
You can automatically create bits of the ODB by passing a struct to the&lt;br /&gt;
&amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; constructor, then calling &amp;lt;code&amp;gt;odb::connect()&amp;lt;/code&amp;gt;, like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Define the ODB structure&lt;br /&gt;
midas::odb o = {&lt;br /&gt;
   {&amp;quot;Int32 Key&amp;quot;, 42},&lt;br /&gt;
   {&amp;quot;Bool Key&amp;quot;, true},&lt;br /&gt;
   {&amp;quot;Subdir&amp;quot;, {&lt;br /&gt;
      {&amp;quot;Float key&amp;quot;, 1.2f},     // floats must be explicitly specified&lt;br /&gt;
   }},&lt;br /&gt;
   {&amp;quot;Int Array&amp;quot;, {1, 2, 3}},&lt;br /&gt;
   {&amp;quot;Double Array&amp;quot;, {1.2, 2.3, 3.4}},&lt;br /&gt;
   {&amp;quot;String Array&amp;quot;, {&amp;quot;Hello1&amp;quot;, &amp;quot;Hello2&amp;quot;, &amp;quot;Hello3&amp;quot;}},&lt;br /&gt;
   {&amp;quot;Large Array&amp;quot;, std::array&amp;lt;int, 10&amp;gt;{} },   // array with explicit size&lt;br /&gt;
   {&amp;quot;Large String&amp;quot;, std::string(63, &#039;\0&#039;) },  // string with explicit size&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Then sync the structure. This function&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// If you make the `write_defaults` argument true, then the function&lt;br /&gt;
// - overwrites the value of any keys that are in the ODB with the value in your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
// - deletes any keys that are in the ODB but not your code&lt;br /&gt;
// - updates the order of keys in the ODB to match your code&lt;br /&gt;
o.connect_and_fix_structure(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the ODB path in teh odb::connect() call must start with a &#039;/&#039;. The ODB root path like &amp;lt;code&amp;gt;o.connect(&amp;quot;/&amp;quot;);&amp;lt;/code&amp;gt; is not allowed in this call.&lt;br /&gt;
&lt;br /&gt;
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
existing_key[&amp;quot;MyNewSubKey&amp;quot;] = 1.23;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also create new keys by providing a default value when reading a value. If the key doesn&#039;t already exist, the default value will be used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
double val = existing_key[&amp;quot;MyNewSubKey&amp;quot;](1.23);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Iterating over subkeys ==&lt;br /&gt;
&lt;br /&gt;
You can iterate over subkeys using normal iterator functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
for (auto it = exp.begin(); it != exp.end(); it++) {&lt;br /&gt;
   midas::odb&amp;amp; subkey = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (midas::odb&amp;amp; subkey : exp) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can check whether a subkey exists using &amp;lt;code&amp;gt;odb::is_subkey()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Callback functions ==&lt;br /&gt;
&lt;br /&gt;
You may also set up callback functions that are called whenever a value&lt;br /&gt;
changes, using the &amp;lt;code&amp;gt;odb::watch()&amp;lt;/code&amp;gt; function. Note that you must call&lt;br /&gt;
&amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; (from midas.h) periodically for this to work - deep down it&lt;br /&gt;
is &amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; itself that calls your callback function.&lt;br /&gt;
&lt;br /&gt;
The callback functions can either be a &amp;quot;normal&amp;quot; function, a C++ lambda, or a member function of a C++ class.&lt;br /&gt;
In all cases it should accept one argument - a &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object (passed&lt;br /&gt;
by reference) that contains the new state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a lambda:&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch([](midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a &amp;quot;normal&amp;quot; function:&lt;br /&gt;
void my_function(midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch(my_function);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a member function of a class:&lt;br /&gt;
#include &amp;lt;functional&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void MyClass::my_function(midas::odb&amp;amp; arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MyClass::some_initialisation_code() {&lt;br /&gt;
  // Arguments to std::bind() are: &amp;quot;member function to call&amp;quot;, &amp;quot;object to call that function on&amp;quot;, &amp;quot;placeholder for midas::odb arg&amp;quot;&lt;br /&gt;
  std::function&amp;lt;void(midas::odb&amp;amp;)&amp;gt; callback = std::bind(&amp;amp;MyClass::my_function, this, std::placeholders::_1);&lt;br /&gt;
&lt;br /&gt;
  midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
  to_watch.watch(callback);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utility functions ==&lt;br /&gt;
&lt;br /&gt;
There are various utility functions which can be used:&lt;br /&gt;
&lt;br /&gt;
==== void odb::create(const char *name, int type) ====&lt;br /&gt;
&lt;br /&gt;
Simple wrapper around db_create_key() to create a single key in the ODB. &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; is one of TID_xxx.&lt;br /&gt;
&lt;br /&gt;
==== void odb::delete_key() ====&lt;br /&gt;
&lt;br /&gt;
This member function of a midas::odb object deletes that object from the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb o(&amp;quot;/Some/ODB/Path&amp;quot;);&lt;br /&gt;
o.delete_key();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== int odb::delete_key(const std::string &amp;amp;name) ====&lt;br /&gt;
&lt;br /&gt;
This function deletes a key or a subtree in the ODB passed by its path in the &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.&lt;br /&gt;
&lt;br /&gt;
==== bool odb::exists(const std::string *name) ====&lt;br /&gt;
&lt;br /&gt;
This boolean function checks if a key given by its name exists in the ODB.&lt;br /&gt;
&lt;br /&gt;
==== void odb::load(const std::string &amp;amp;filename, const std::string &amp;amp;odb_path) ====&lt;br /&gt;
&lt;br /&gt;
Load a part (or the whole) ODB from a .JSON or .XML file. Put it under a certain path in the ODB.&lt;br /&gt;
&lt;br /&gt;
==== void odb::save(const std::string &amp;amp;filename) ====&lt;br /&gt;
&lt;br /&gt;
Save a midas::odb object to a file in JSON format. Save the whole ODB with:&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb o(&amp;quot;/&amp;quot;);&lt;br /&gt;
o.save(&amp;quot;odb.json&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====&lt;br /&gt;
&lt;br /&gt;
These functions set and retrieve the debug flag. If the debug flag is &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.&lt;br /&gt;
&lt;br /&gt;
== Example code ==&lt;br /&gt;
&lt;br /&gt;
A full working example exploring most of the features can be found in&lt;br /&gt;
&amp;lt;code&amp;gt;odbxx/odbxx_test.cxx&amp;lt;/code&amp;gt;. The test executable will be compiled as&lt;br /&gt;
&amp;lt;code&amp;gt;build/odbxx/odbxx_test&amp;lt;/code&amp;gt; (it is not installed in the `bin` directory).&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3536</id>
		<title>Odbxx</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3536"/>
		<updated>2025-07-18T08:37:59Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Utility functions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a &amp;quot;magic&amp;quot; map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.&lt;br /&gt;
&lt;br /&gt;
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/examples/odbxx/odbxx_test.cxx odbxx_test.cxx].&lt;br /&gt;
&lt;br /&gt;
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]]  page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.&lt;br /&gt;
&lt;br /&gt;
== Basic usage ==&lt;br /&gt;
&lt;br /&gt;
The simplest usage is like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Grab a single value from the ODB&lt;br /&gt;
int r = midas::odb(&amp;quot;/Runinfo/Run number&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Grab a subtree from the ODB&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Simple read using the subtree, causes a re-read of the ODB value in the background&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The current transition timeout is &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&lt;br /&gt;
// Make a change. The new value is automatically sent to the ODB.&lt;br /&gt;
// Most C++ operators are supported (++, += etc), or you can do a simple&lt;br /&gt;
// re-assignment like `exp[&amp;quot;Transition timeout&amp;quot;] = 12345;`.&lt;br /&gt;
exp[&amp;quot;Transition timeout&amp;quot;] += 100;&lt;br /&gt;
&lt;br /&gt;
// Read the new value&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The transition timeout is now &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that keys in the ODB are case insensitive, thus &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; will give you the same key as &amp;lt;code&amp;gt;exp[&amp;quot;Transition TIMEOUT&amp;quot;]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;int curr_timeout = exp[&amp;quot;Transition timeout&amp;quot;];&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The ODB directory you connect to (&amp;quot;/Experiment&amp;quot; in the above example), has to start with a &amp;quot;/&amp;quot; to tell the midas::odb object&lt;br /&gt;
to connect directly to the ODB. Otherwise, a simple local midas::odb string object gets created without any connection to the ODB.&lt;br /&gt;
&lt;br /&gt;
== Automatic refreshing ==&lt;br /&gt;
&lt;br /&gt;
You may temporarily disable the automatic updating to/from the ODB&lt;br /&gt;
using &amp;lt;code&amp;gt;odb::set_auto_refresh_write(false)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;odb::set_auto_refresh_read(false)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If auto-refresh is enabled (the default), your new values are sent to&lt;br /&gt;
the ODB as soon as you touch the value in the &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object. The ODB&lt;br /&gt;
is queried for new values whenever you access the value. In the above&lt;br /&gt;
example, the ODB is queried 4 times (during construction of &amp;lt;code&amp;gt;exp&amp;lt;/code&amp;gt;, and&lt;br /&gt;
each time &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is mentioned), and written to 1&lt;br /&gt;
time (when &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is assigned to).&lt;br /&gt;
&lt;br /&gt;
See the [[#Callback functions]] section below for details on how to have a function&lt;br /&gt;
called when a value changes.&lt;br /&gt;
&lt;br /&gt;
== Arrays/vectors ==&lt;br /&gt;
&lt;br /&gt;
ODB arrays are represented by std vectors.&lt;br /&gt;
&lt;br /&gt;
You can access/edit individual elements using []:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;][1] = 1.2;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can completely re-assign content using a std::vector or std::array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(10);&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can resize arrays using &amp;lt;code&amp;gt;odb::resize()&amp;lt;/code&amp;gt;. If the existing array is longer,&lt;br /&gt;
it will be truncated; if shorter it will be extended with default values&lt;br /&gt;
(0 or an empty string).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;].resize(5); // Now is 5 elements long&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that arithmetic operators are supported for arrays, and will apply&lt;br /&gt;
the operation to ALL ELEMENTS IN THE ARRAY:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Create the vector&lt;br /&gt;
std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(2);&lt;br /&gt;
vec[0] = 3;&lt;br /&gt;
vec[1] = 5;&lt;br /&gt;
&lt;br /&gt;
// Assign in ODB&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&lt;br /&gt;
// Multiply ALL elements by 2&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] *= 2;&lt;br /&gt;
&lt;br /&gt;
// odb[&amp;quot;Example&amp;quot;] now contains {6, 10}.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can directly iterate over arrays/vectors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
for (auto it = o[&amp;quot;Int Array&amp;quot;].begin(); it != o[&amp;quot;Int Array&amp;quot;].end(); it++) {&lt;br /&gt;
   int val = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (int val : o[&amp;quot;Int Array&amp;quot;]) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Strings ==&lt;br /&gt;
&lt;br /&gt;
Strings in the ODB are returned as std::string (unlike the midas.h &amp;lt;code&amp;gt;db_get_value()&amp;lt;/code&amp;gt;&lt;br /&gt;
family of functions, where strings are returned as char*). You may have vectors of strings.&lt;br /&gt;
&lt;br /&gt;
== Creating new bits of the ODB ==&lt;br /&gt;
&lt;br /&gt;
You can automatically create bits of the ODB by passing a struct to the&lt;br /&gt;
&amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; constructor, then calling &amp;lt;code&amp;gt;odb::connect()&amp;lt;/code&amp;gt;, like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Define the ODB structure&lt;br /&gt;
midas::odb o = {&lt;br /&gt;
   {&amp;quot;Int32 Key&amp;quot;, 42},&lt;br /&gt;
   {&amp;quot;Bool Key&amp;quot;, true},&lt;br /&gt;
   {&amp;quot;Subdir&amp;quot;, {&lt;br /&gt;
      {&amp;quot;Float key&amp;quot;, 1.2f},     // floats must be explicitly specified&lt;br /&gt;
   }},&lt;br /&gt;
   {&amp;quot;Int Array&amp;quot;, {1, 2, 3}},&lt;br /&gt;
   {&amp;quot;Double Array&amp;quot;, {1.2, 2.3, 3.4}},&lt;br /&gt;
   {&amp;quot;String Array&amp;quot;, {&amp;quot;Hello1&amp;quot;, &amp;quot;Hello2&amp;quot;, &amp;quot;Hello3&amp;quot;}},&lt;br /&gt;
   {&amp;quot;Large Array&amp;quot;, std::array&amp;lt;int, 10&amp;gt;{} },   // array with explicit size&lt;br /&gt;
   {&amp;quot;Large String&amp;quot;, std::string(63, &#039;\0&#039;) },  // string with explicit size&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Then sync the structure. This function&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// If you make the `write_defaults` argument true, then the function&lt;br /&gt;
// - overwrites the value of any keys that are in the ODB with the value in your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
// - deletes any keys that are in the ODB but not your code&lt;br /&gt;
// - updates the order of keys in the ODB to match your code&lt;br /&gt;
o.connect_and_fix_structure(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the ODB path in teh odb::connect() call must start with a &#039;/&#039;. The ODB root path like &amp;lt;code&amp;gt;o.connect(&amp;quot;/&amp;quot;);&amp;lt;/code&amp;gt; is not allowed in this call.&lt;br /&gt;
&lt;br /&gt;
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
existing_key[&amp;quot;MyNewSubKey&amp;quot;] = 1.23;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also create new keys by providing a default value when reading a value. If the key doesn&#039;t already exist, the default value will be used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
double val = existing_key[&amp;quot;MyNewSubKey&amp;quot;](1.23);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Iterating over subkeys ==&lt;br /&gt;
&lt;br /&gt;
You can iterate over subkeys using normal iterator functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
for (auto it = exp.begin(); it != exp.end(); it++) {&lt;br /&gt;
   midas::odb&amp;amp; subkey = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (midas::odb&amp;amp; subkey : exp) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can check whether a subkey exists using &amp;lt;code&amp;gt;odb::is_subkey()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Callback functions ==&lt;br /&gt;
&lt;br /&gt;
You may also set up callback functions that are called whenever a value&lt;br /&gt;
changes, using the &amp;lt;code&amp;gt;odb::watch()&amp;lt;/code&amp;gt; function. Note that you must call&lt;br /&gt;
&amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; (from midas.h) periodically for this to work - deep down it&lt;br /&gt;
is &amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; itself that calls your callback function.&lt;br /&gt;
&lt;br /&gt;
The callback functions can either be a &amp;quot;normal&amp;quot; function, a C++ lambda, or a member function of a C++ class.&lt;br /&gt;
In all cases it should accept one argument - a &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object (passed&lt;br /&gt;
by reference) that contains the new state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a lambda:&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch([](midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a &amp;quot;normal&amp;quot; function:&lt;br /&gt;
void my_function(midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch(my_function);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a member function of a class:&lt;br /&gt;
#include &amp;lt;functional&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void MyClass::my_function(midas::odb&amp;amp; arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MyClass::some_initialisation_code() {&lt;br /&gt;
  // Arguments to std::bind() are: &amp;quot;member function to call&amp;quot;, &amp;quot;object to call that function on&amp;quot;, &amp;quot;placeholder for midas::odb arg&amp;quot;&lt;br /&gt;
  std::function&amp;lt;void(midas::odb&amp;amp;)&amp;gt; callback = std::bind(&amp;amp;MyClass::my_function, this, std::placeholders::_1);&lt;br /&gt;
&lt;br /&gt;
  midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
  to_watch.watch(callback);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utility functions ==&lt;br /&gt;
&lt;br /&gt;
There are various utility functions which can be used:&lt;br /&gt;
&lt;br /&gt;
==== void odb::create(const char *name, int type) ====&lt;br /&gt;
&lt;br /&gt;
Simple wrapper around db_create_key() to create a single key in the ODB. &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; is one of TID_xxx.&lt;br /&gt;
&lt;br /&gt;
==== void odb::delete_key() ====&lt;br /&gt;
&lt;br /&gt;
This member function of a midas::odb object deletes that object from the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb o(&amp;quot;/Some/ODB/Path&amp;quot;);&lt;br /&gt;
o.delete_key();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== int odb::delete_key(const std::string &amp;amp;name) ====&lt;br /&gt;
&lt;br /&gt;
This function deletes a key or a subtree in the ODB passed by its path in the &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.&lt;br /&gt;
&lt;br /&gt;
==== bool odb::exists(const std::string *name) ====&lt;br /&gt;
&lt;br /&gt;
This boolean function checks if a key given by its name exists in the ODB.&lt;br /&gt;
&lt;br /&gt;
==== void odb::load(const std::string &amp;amp;filename, const std::string &amp;amp;odb_path) ====&lt;br /&gt;
&lt;br /&gt;
Load a part (or the whole) ODB from a .JSON or .XML file. Put it under a certain path in the ODB.&lt;br /&gt;
&lt;br /&gt;
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====&lt;br /&gt;
&lt;br /&gt;
These functions set and retrieve the debug flag. If the debug flag is &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.&lt;br /&gt;
&lt;br /&gt;
== Example code ==&lt;br /&gt;
&lt;br /&gt;
A full working example exploring most of the features can be found in&lt;br /&gt;
&amp;lt;code&amp;gt;odbxx/odbxx_test.cxx&amp;lt;/code&amp;gt;. The test executable will be compiled as&lt;br /&gt;
&amp;lt;code&amp;gt;build/odbxx/odbxx_test&amp;lt;/code&amp;gt; (it is not installed in the `bin` directory).&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3535</id>
		<title>Odbxx</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3535"/>
		<updated>2025-07-18T08:25:18Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Basic usage */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a &amp;quot;magic&amp;quot; map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.&lt;br /&gt;
&lt;br /&gt;
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/examples/odbxx/odbxx_test.cxx odbxx_test.cxx].&lt;br /&gt;
&lt;br /&gt;
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]]  page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.&lt;br /&gt;
&lt;br /&gt;
== Basic usage ==&lt;br /&gt;
&lt;br /&gt;
The simplest usage is like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Grab a single value from the ODB&lt;br /&gt;
int r = midas::odb(&amp;quot;/Runinfo/Run number&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Grab a subtree from the ODB&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Simple read using the subtree, causes a re-read of the ODB value in the background&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The current transition timeout is &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&lt;br /&gt;
// Make a change. The new value is automatically sent to the ODB.&lt;br /&gt;
// Most C++ operators are supported (++, += etc), or you can do a simple&lt;br /&gt;
// re-assignment like `exp[&amp;quot;Transition timeout&amp;quot;] = 12345;`.&lt;br /&gt;
exp[&amp;quot;Transition timeout&amp;quot;] += 100;&lt;br /&gt;
&lt;br /&gt;
// Read the new value&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The transition timeout is now &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that keys in the ODB are case insensitive, thus &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; will give you the same key as &amp;lt;code&amp;gt;exp[&amp;quot;Transition TIMEOUT&amp;quot;]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;int curr_timeout = exp[&amp;quot;Transition timeout&amp;quot;];&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The ODB directory you connect to (&amp;quot;/Experiment&amp;quot; in the above example), has to start with a &amp;quot;/&amp;quot; to tell the midas::odb object&lt;br /&gt;
to connect directly to the ODB. Otherwise, a simple local midas::odb string object gets created without any connection to the ODB.&lt;br /&gt;
&lt;br /&gt;
== Automatic refreshing ==&lt;br /&gt;
&lt;br /&gt;
You may temporarily disable the automatic updating to/from the ODB&lt;br /&gt;
using &amp;lt;code&amp;gt;odb::set_auto_refresh_write(false)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;odb::set_auto_refresh_read(false)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If auto-refresh is enabled (the default), your new values are sent to&lt;br /&gt;
the ODB as soon as you touch the value in the &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object. The ODB&lt;br /&gt;
is queried for new values whenever you access the value. In the above&lt;br /&gt;
example, the ODB is queried 4 times (during construction of &amp;lt;code&amp;gt;exp&amp;lt;/code&amp;gt;, and&lt;br /&gt;
each time &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is mentioned), and written to 1&lt;br /&gt;
time (when &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is assigned to).&lt;br /&gt;
&lt;br /&gt;
See the [[#Callback functions]] section below for details on how to have a function&lt;br /&gt;
called when a value changes.&lt;br /&gt;
&lt;br /&gt;
== Arrays/vectors ==&lt;br /&gt;
&lt;br /&gt;
ODB arrays are represented by std vectors.&lt;br /&gt;
&lt;br /&gt;
You can access/edit individual elements using []:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;][1] = 1.2;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can completely re-assign content using a std::vector or std::array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(10);&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can resize arrays using &amp;lt;code&amp;gt;odb::resize()&amp;lt;/code&amp;gt;. If the existing array is longer,&lt;br /&gt;
it will be truncated; if shorter it will be extended with default values&lt;br /&gt;
(0 or an empty string).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;].resize(5); // Now is 5 elements long&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that arithmetic operators are supported for arrays, and will apply&lt;br /&gt;
the operation to ALL ELEMENTS IN THE ARRAY:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Create the vector&lt;br /&gt;
std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(2);&lt;br /&gt;
vec[0] = 3;&lt;br /&gt;
vec[1] = 5;&lt;br /&gt;
&lt;br /&gt;
// Assign in ODB&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&lt;br /&gt;
// Multiply ALL elements by 2&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] *= 2;&lt;br /&gt;
&lt;br /&gt;
// odb[&amp;quot;Example&amp;quot;] now contains {6, 10}.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can directly iterate over arrays/vectors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
for (auto it = o[&amp;quot;Int Array&amp;quot;].begin(); it != o[&amp;quot;Int Array&amp;quot;].end(); it++) {&lt;br /&gt;
   int val = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (int val : o[&amp;quot;Int Array&amp;quot;]) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Strings ==&lt;br /&gt;
&lt;br /&gt;
Strings in the ODB are returned as std::string (unlike the midas.h &amp;lt;code&amp;gt;db_get_value()&amp;lt;/code&amp;gt;&lt;br /&gt;
family of functions, where strings are returned as char*). You may have vectors of strings.&lt;br /&gt;
&lt;br /&gt;
== Creating new bits of the ODB ==&lt;br /&gt;
&lt;br /&gt;
You can automatically create bits of the ODB by passing a struct to the&lt;br /&gt;
&amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; constructor, then calling &amp;lt;code&amp;gt;odb::connect()&amp;lt;/code&amp;gt;, like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Define the ODB structure&lt;br /&gt;
midas::odb o = {&lt;br /&gt;
   {&amp;quot;Int32 Key&amp;quot;, 42},&lt;br /&gt;
   {&amp;quot;Bool Key&amp;quot;, true},&lt;br /&gt;
   {&amp;quot;Subdir&amp;quot;, {&lt;br /&gt;
      {&amp;quot;Float key&amp;quot;, 1.2f},     // floats must be explicitly specified&lt;br /&gt;
   }},&lt;br /&gt;
   {&amp;quot;Int Array&amp;quot;, {1, 2, 3}},&lt;br /&gt;
   {&amp;quot;Double Array&amp;quot;, {1.2, 2.3, 3.4}},&lt;br /&gt;
   {&amp;quot;String Array&amp;quot;, {&amp;quot;Hello1&amp;quot;, &amp;quot;Hello2&amp;quot;, &amp;quot;Hello3&amp;quot;}},&lt;br /&gt;
   {&amp;quot;Large Array&amp;quot;, std::array&amp;lt;int, 10&amp;gt;{} },   // array with explicit size&lt;br /&gt;
   {&amp;quot;Large String&amp;quot;, std::string(63, &#039;\0&#039;) },  // string with explicit size&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Then sync the structure. This function&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// If you make the `write_defaults` argument true, then the function&lt;br /&gt;
// - overwrites the value of any keys that are in the ODB with the value in your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
// - deletes any keys that are in the ODB but not your code&lt;br /&gt;
// - updates the order of keys in the ODB to match your code&lt;br /&gt;
o.connect_and_fix_structure(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the ODB path in teh odb::connect() call must start with a &#039;/&#039;. The ODB root path like &amp;lt;code&amp;gt;o.connect(&amp;quot;/&amp;quot;);&amp;lt;/code&amp;gt; is not allowed in this call.&lt;br /&gt;
&lt;br /&gt;
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
existing_key[&amp;quot;MyNewSubKey&amp;quot;] = 1.23;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also create new keys by providing a default value when reading a value. If the key doesn&#039;t already exist, the default value will be used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
double val = existing_key[&amp;quot;MyNewSubKey&amp;quot;](1.23);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Iterating over subkeys ==&lt;br /&gt;
&lt;br /&gt;
You can iterate over subkeys using normal iterator functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
for (auto it = exp.begin(); it != exp.end(); it++) {&lt;br /&gt;
   midas::odb&amp;amp; subkey = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (midas::odb&amp;amp; subkey : exp) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can check whether a subkey exists using &amp;lt;code&amp;gt;odb::is_subkey()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Callback functions ==&lt;br /&gt;
&lt;br /&gt;
You may also set up callback functions that are called whenever a value&lt;br /&gt;
changes, using the &amp;lt;code&amp;gt;odb::watch()&amp;lt;/code&amp;gt; function. Note that you must call&lt;br /&gt;
&amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; (from midas.h) periodically for this to work - deep down it&lt;br /&gt;
is &amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; itself that calls your callback function.&lt;br /&gt;
&lt;br /&gt;
The callback functions can either be a &amp;quot;normal&amp;quot; function, a C++ lambda, or a member function of a C++ class.&lt;br /&gt;
In all cases it should accept one argument - a &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object (passed&lt;br /&gt;
by reference) that contains the new state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a lambda:&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch([](midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a &amp;quot;normal&amp;quot; function:&lt;br /&gt;
void my_function(midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch(my_function);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a member function of a class:&lt;br /&gt;
#include &amp;lt;functional&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void MyClass::my_function(midas::odb&amp;amp; arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MyClass::some_initialisation_code() {&lt;br /&gt;
  // Arguments to std::bind() are: &amp;quot;member function to call&amp;quot;, &amp;quot;object to call that function on&amp;quot;, &amp;quot;placeholder for midas::odb arg&amp;quot;&lt;br /&gt;
  std::function&amp;lt;void(midas::odb&amp;amp;)&amp;gt; callback = std::bind(&amp;amp;MyClass::my_function, this, std::placeholders::_1);&lt;br /&gt;
&lt;br /&gt;
  midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
  to_watch.watch(callback);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utility functions ==&lt;br /&gt;
&lt;br /&gt;
There are various utility functions which can be used:&lt;br /&gt;
&lt;br /&gt;
==== void odb::create(const char *name, int type) ====&lt;br /&gt;
&lt;br /&gt;
Simple wrapper around db_create_key() to create a single key in the ODB. &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; is one of TID_xxx.&lt;br /&gt;
&lt;br /&gt;
==== void odb::delete_key() ====&lt;br /&gt;
&lt;br /&gt;
This member function of a midas::odb object deletes that object from the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb o(&amp;quot;/Some/ODB/Path&amp;quot;);&lt;br /&gt;
o.delete_key();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== int odb::delete_key(const std::string &amp;amp;name) ====&lt;br /&gt;
&lt;br /&gt;
This function deletes a key or a subtree in the ODB passed by its path in the &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.&lt;br /&gt;
&lt;br /&gt;
==== bool odb::exists(const std::string *name) ====&lt;br /&gt;
&lt;br /&gt;
This boolean function checks if a key given by its name exists in the ODB.&lt;br /&gt;
&lt;br /&gt;
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====&lt;br /&gt;
&lt;br /&gt;
These functions set and retrieve the debug flag. If the debug flag is &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.&lt;br /&gt;
&lt;br /&gt;
== Example code ==&lt;br /&gt;
&lt;br /&gt;
A full working example exploring most of the features can be found in&lt;br /&gt;
&amp;lt;code&amp;gt;odbxx/odbxx_test.cxx&amp;lt;/code&amp;gt;. The test executable will be compiled as&lt;br /&gt;
&amp;lt;code&amp;gt;build/odbxx/odbxx_test&amp;lt;/code&amp;gt; (it is not installed in the `bin` directory).&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3515</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3515"/>
		<updated>2025-05-08T09:12:26Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* modbbutton */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.&lt;br /&gt;
&lt;br /&gt;
We note that MIDAS has provided a number of different ways of providing custom pages over the years.  A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a &amp;quot;virtual&amp;quot; X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.&lt;br /&gt;
&lt;br /&gt;
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.&lt;br /&gt;
&lt;br /&gt;
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in&lt;br /&gt;
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: With the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new tab/window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.&lt;br /&gt;
&lt;br /&gt;
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions.  The advantages of using these modb* javascript functions are&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient.  In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call).  It will also require more coding to implement the custom page with only MjsonRPC calls.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Dynamic ODB paths ==&lt;br /&gt;
&lt;br /&gt;
The path of a custom control is static, meaning it has to be written directly inside the control via &amp;lt;code&amp;gt;data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;lt;/code&amp;gt;. There can be however cases where the path is only known at runtime. For these cases, the path can be a user function which gets called during each update of the control and has to return the current ODB path. This can be useful for equipment values with names. The standard scheme of MIDAS is to have slow control variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Variables&amp;lt;/code&amp;gt; and the names of these variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names&amp;lt;/code&amp;gt;. If there are several different arrays under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;, you might also have something like &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names XXX&amp;lt;/code&amp;gt; where &amp;lt;code&amp;gt;XXX&amp;lt;/code&amp;gt; is the name of the key under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;. If one wants to use a name of a variable rather than its index, the dynamic path function can be uses like in the following example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
   let names;&lt;br /&gt;
&lt;br /&gt;
   function getNames() {&lt;br /&gt;
      mjsonrpc_db_get_value(&amp;quot;/Equipment/.../Settings/Names&amp;quot;).then(function (rpc) {&lt;br /&gt;
         names = rpc.result.data[0];&lt;br /&gt;
         mhttpd_init(&#039;Custom Page Example&#039;);&lt;br /&gt;
      });&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   function getpath(p) {&lt;br /&gt;
      if (mscbNames)&lt;br /&gt;
         return `/Equipment/.../Variables/MSCB[${names.indexOf(p)}]`;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;getNames();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the page is initialized, the function &amp;lt;code&amp;gt;getNames()&amp;lt;/code&amp;gt; is called, which obtains the &amp;lt;code&amp;gt;Names&amp;lt;/code&amp;gt; array from the ODB and stores it into an array. After the names have been received, the function calls the usual &amp;lt;code&amp;gt;mhttpd_init()&amp;lt;/code&amp;gt; function. The &amp;quot;modbvalue&amp;quot; path is now set to &amp;lt;code&amp;gt;data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;lt;/code&amp;gt;. This function does a lookup in the &amp;lt;code&amp;gt;names&amp;lt;/code&amp;gt; array and returns the proper index, which is then used to access the corresponding value in the variables array. This method has the advantage that if the indices of an array change (e.g. by adding new variables at the front), the code does not have to update any index since the indices are calculated dynamically.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 1  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3514</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3514"/>
		<updated>2025-05-06T10:19:34Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Dynamic ODB paths */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.&lt;br /&gt;
&lt;br /&gt;
We note that MIDAS has provided a number of different ways of providing custom pages over the years.  A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a &amp;quot;virtual&amp;quot; X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.&lt;br /&gt;
&lt;br /&gt;
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.&lt;br /&gt;
&lt;br /&gt;
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in&lt;br /&gt;
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: With the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new tab/window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.&lt;br /&gt;
&lt;br /&gt;
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions.  The advantages of using these modb* javascript functions are&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient.  In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call).  It will also require more coding to implement the custom page with only MjsonRPC calls.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Dynamic ODB paths ==&lt;br /&gt;
&lt;br /&gt;
The path of a custom control is static, meaning it has to be written directly inside the control via &amp;lt;code&amp;gt;data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;lt;/code&amp;gt;. There can be however cases where the path is only known at runtime. For these cases, the path can be a user function which gets called during each update of the control and has to return the current ODB path. This can be useful for equipment values with names. The standard scheme of MIDAS is to have slow control variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Variables&amp;lt;/code&amp;gt; and the names of these variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names&amp;lt;/code&amp;gt;. If there are several different arrays under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;, you might also have something like &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names XXX&amp;lt;/code&amp;gt; where &amp;lt;code&amp;gt;XXX&amp;lt;/code&amp;gt; is the name of the key under &amp;lt;code&amp;gt;Variables&amp;lt;/code&amp;gt;. If one wants to use a name of a variable rather than its index, the dynamic path function can be uses like in the following example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
   let names;&lt;br /&gt;
&lt;br /&gt;
   function getNames() {&lt;br /&gt;
      mjsonrpc_db_get_value(&amp;quot;/Equipment/.../Settings/Names&amp;quot;).then(function (rpc) {&lt;br /&gt;
         names = rpc.result.data[0];&lt;br /&gt;
         mhttpd_init(&#039;Custom Page Example&#039;);&lt;br /&gt;
      });&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   function getpath(p) {&lt;br /&gt;
      if (mscbNames)&lt;br /&gt;
         return `/Equipment/.../Variables/MSCB[${names.indexOf(p)}]`;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;getNames();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the page is initialized, the function &amp;lt;code&amp;gt;getNames()&amp;lt;/code&amp;gt; is called, which obtains the &amp;lt;code&amp;gt;Names&amp;lt;/code&amp;gt; array from the ODB and stores it into an array. After the names have been received, the function calls the usual &amp;lt;code&amp;gt;mhttpd_init()&amp;lt;/code&amp;gt; function. The &amp;quot;modbvalue&amp;quot; path is now set to &amp;lt;code&amp;gt;data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;lt;/code&amp;gt;. This function does a lookup in the &amp;lt;code&amp;gt;names&amp;lt;/code&amp;gt; array and returns the proper index, which is then used to access the corresponding value in the variables array. This method has the advantage that if the indices of an array change (e.g. by adding new variables at the front), the code does not have to update any index since the indices are calculated dynamically.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 1  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3513</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3513"/>
		<updated>2025-05-02T13:59:10Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Changing properties of controls dynamically */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.&lt;br /&gt;
&lt;br /&gt;
We note that MIDAS has provided a number of different ways of providing custom pages over the years.  A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a &amp;quot;virtual&amp;quot; X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.&lt;br /&gt;
&lt;br /&gt;
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.&lt;br /&gt;
&lt;br /&gt;
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in&lt;br /&gt;
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: With the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new tab/window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.&lt;br /&gt;
&lt;br /&gt;
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions.  The advantages of using these modb* javascript functions are&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient.  In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call).  It will also require more coding to implement the custom page with only MjsonRPC calls.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Dynamic ODB paths ==&lt;br /&gt;
&lt;br /&gt;
The path of a custom control is static, meaning it has to be written directly inside the control via &amp;lt;code&amp;gt;data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;lt;/code&amp;gt;. There can be however cases where the path is only known at runtime. For these cases, the path can be a user function which gets called during each update of the control and has to return the current ODB path. This can be useful for equipment values with names. The standard scheme of MIDAS is to have slow control variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Variables&amp;lt;/code&amp;gt; and the names of these variables under &amp;lt;code&amp;gt;/Equipment/&amp;lt;name&amp;gt;/Settings/Names&amp;lt;/code&amp;gt;. If one wants to use a name of a variable rather than its index, the dynamic path function can be uses like in the following example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
   let names;&lt;br /&gt;
&lt;br /&gt;
   function getNames() {&lt;br /&gt;
      mjsonrpc_db_get_value(&amp;quot;/Equipment/.../Settings/Names&amp;quot;).then(function (rpc) {&lt;br /&gt;
         names = rpc.result.data[0];&lt;br /&gt;
         mhttpd_init(&#039;Custom Page Example&#039;);&lt;br /&gt;
      });&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   function getpath(p) {&lt;br /&gt;
      if (mscbNames)&lt;br /&gt;
         return `/Equipment/.../Variables/MSCB[${names.indexOf(p)}]`;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;getNames();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When the page is initialized, the function &amp;lt;code&amp;gt;getNames()&amp;lt;/code&amp;gt; is called, which obtains the &amp;lt;code&amp;gt;Names&amp;lt;/code&amp;gt; array from the ODB and stores it into an array. After the names have been received, the function calls the usual &amp;lt;code&amp;gt;mhttpd_init()&amp;lt;/code&amp;gt; function. The &amp;quot;modbvalue&amp;quot; path is now set to &amp;lt;code&amp;gt;data-odb-path=&amp;quot;getpath(&#039;ADC1&#039;)&amp;quot;&amp;lt;/code&amp;gt;. This function does a lookup in the &amp;lt;code&amp;gt;names&amp;lt;/code&amp;gt; array and returns the proper index, which is then used to access the corresponding value in the variables array. This method has the advantage that if the indices of an array change (e.g. by adding new variables at the front), the code does not have to update any index since the indices are calculated dynamically.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 1  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3512</id>
		<title>Odbxx</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Odbxx&amp;diff=3512"/>
		<updated>2025-04-30T11:10:32Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: Added note about case-insensitivity&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A C++11 object-oriented interface to the [[ODB|ODB (online database)]] was introduced in May 2020. You can think of it like a &amp;quot;magic&amp;quot; map/dictionary that automatically sends changes you make to the ODB, and receives updates that others have made.&lt;br /&gt;
&lt;br /&gt;
The header for this odbxx interface is at [https://bitbucket.org/tmidas/midas/src/develop/include/odbxx.h odbxx.h] and example usage in [https://bitbucket.org/tmidas/midas/src/develop/examples/odbxx/odbxx_test.cxx odbxx_test.cxx].&lt;br /&gt;
&lt;br /&gt;
You can find more details about the ODB on the [[ODB_Access_and_Use|ODB Access and Use]]  page, which includes links to the command-line, javascript, python, and non-object C++ interfaces.&lt;br /&gt;
&lt;br /&gt;
== Basic usage ==&lt;br /&gt;
&lt;br /&gt;
The simplest usage is like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Grab a bit of the ODB&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Simple read&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The current transition timeout is &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&lt;br /&gt;
// Make a change. The new value is automatically sent to the ODB.&lt;br /&gt;
// Most C++ operators are supported (++, += etc), or you can do a simple&lt;br /&gt;
// re-assignment like `exp[&amp;quot;Transition timeout&amp;quot;] = 12345;`.&lt;br /&gt;
exp[&amp;quot;Transition timeout&amp;quot;] += 100;&lt;br /&gt;
&lt;br /&gt;
// Read the new value&lt;br /&gt;
std::cout &amp;lt;&amp;lt; &amp;quot;The transition timeout is now &amp;quot; &amp;lt;&amp;lt; exp[&amp;quot;Transition timeout&amp;quot;] &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that keys in the ODB are case insensitive, thus &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; will give you the same key as &amp;lt;code&amp;gt;exp[&amp;quot;Transition TIMEOUT&amp;quot;]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can automatically cast to regular data types (int, double) etc if you want a copy of the value to work with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;int curr_timeout = exp[&amp;quot;Transition timeout&amp;quot;];&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: The ODB directory you connect to (&amp;quot;/Experiment&amp;quot; in the above example), has to start with a &amp;quot;/&amp;quot; to tell the midas::odb object&lt;br /&gt;
to connect directly to the ODB. Otherwise, a simple local midas::odb string object gets created without any connection to the ODB.&lt;br /&gt;
&lt;br /&gt;
== Automatic refreshing ==&lt;br /&gt;
&lt;br /&gt;
You may temporarily disable the automatic updating to/from the ODB&lt;br /&gt;
using &amp;lt;code&amp;gt;odb::set_auto_refresh_write(false)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;odb::set_auto_refresh_read(false)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If auto-refresh is enabled (the default), your new values are sent to&lt;br /&gt;
the ODB as soon as you touch the value in the &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object. The ODB&lt;br /&gt;
is queried for new values whenever you access the value. In the above&lt;br /&gt;
example, the ODB is queried 4 times (during construction of &amp;lt;code&amp;gt;exp&amp;lt;/code&amp;gt;, and&lt;br /&gt;
each time &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is mentioned), and written to 1&lt;br /&gt;
time (when &amp;lt;code&amp;gt;exp[&amp;quot;Transition timeout&amp;quot;]&amp;lt;/code&amp;gt; is assigned to).&lt;br /&gt;
&lt;br /&gt;
See the [[#Callback functions]] section below for details on how to have a function&lt;br /&gt;
called when a value changes.&lt;br /&gt;
&lt;br /&gt;
== Arrays/vectors ==&lt;br /&gt;
&lt;br /&gt;
ODB arrays are represented by std vectors.&lt;br /&gt;
&lt;br /&gt;
You can access/edit individual elements using []:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;][1] = 1.2;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can completely re-assign content using a std::vector or std::array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(10);&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can resize arrays using &amp;lt;code&amp;gt;odb::resize()&amp;lt;/code&amp;gt;. If the existing array is longer,&lt;br /&gt;
it will be truncated; if shorter it will be extended with default values&lt;br /&gt;
(0 or an empty string).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;odb[&amp;quot;Example&amp;quot;].resize(5); // Now is 5 elements long&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that arithmetic operators are supported for arrays, and will apply&lt;br /&gt;
the operation to ALL ELEMENTS IN THE ARRAY:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Create the vector&lt;br /&gt;
std::vector&amp;lt;float&amp;gt; vec = std::vector&amp;lt;float&amp;gt;(2);&lt;br /&gt;
vec[0] = 3;&lt;br /&gt;
vec[1] = 5;&lt;br /&gt;
&lt;br /&gt;
// Assign in ODB&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] = vec;&lt;br /&gt;
&lt;br /&gt;
// Multiply ALL elements by 2&lt;br /&gt;
odb[&amp;quot;Example&amp;quot;] *= 2;&lt;br /&gt;
&lt;br /&gt;
// odb[&amp;quot;Example&amp;quot;] now contains {6, 10}.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can directly iterate over arrays/vectors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
for (auto it = o[&amp;quot;Int Array&amp;quot;].begin(); it != o[&amp;quot;Int Array&amp;quot;].end(); it++) {&lt;br /&gt;
   int val = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (int val : o[&amp;quot;Int Array&amp;quot;]) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Strings ==&lt;br /&gt;
&lt;br /&gt;
Strings in the ODB are returned as std::string (unlike the midas.h &amp;lt;code&amp;gt;db_get_value()&amp;lt;/code&amp;gt;&lt;br /&gt;
family of functions, where strings are returned as char*). You may have vectors of strings.&lt;br /&gt;
&lt;br /&gt;
== Creating new bits of the ODB ==&lt;br /&gt;
&lt;br /&gt;
You can automatically create bits of the ODB by passing a struct to the&lt;br /&gt;
&amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; constructor, then calling &amp;lt;code&amp;gt;odb::connect()&amp;lt;/code&amp;gt;, like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Define the ODB structure&lt;br /&gt;
midas::odb o = {&lt;br /&gt;
   {&amp;quot;Int32 Key&amp;quot;, 42},&lt;br /&gt;
   {&amp;quot;Bool Key&amp;quot;, true},&lt;br /&gt;
   {&amp;quot;Subdir&amp;quot;, {&lt;br /&gt;
      {&amp;quot;Float key&amp;quot;, 1.2f},     // floats must be explicitly specified&lt;br /&gt;
   }},&lt;br /&gt;
   {&amp;quot;Int Array&amp;quot;, {1, 2, 3}},&lt;br /&gt;
   {&amp;quot;Double Array&amp;quot;, {1.2, 2.3, 3.4}},&lt;br /&gt;
   {&amp;quot;String Array&amp;quot;, {&amp;quot;Hello1&amp;quot;, &amp;quot;Hello2&amp;quot;, &amp;quot;Hello3&amp;quot;}},&lt;br /&gt;
   {&amp;quot;Large Array&amp;quot;, std::array&amp;lt;int, 10&amp;gt;{} },   // array with explicit size&lt;br /&gt;
   {&amp;quot;Large String&amp;quot;, std::string(63, &#039;\0&#039;) },  // string with explicit size&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Then sync the structure. This function&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// If you make the `write_defaults` argument true, then the function&lt;br /&gt;
// - overwrites the value of any keys that are in the ODB with the value in your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
o.connect(&amp;quot;/Test/Settings&amp;quot;, true);&lt;br /&gt;
&lt;br /&gt;
// The `connect_and_fix_structure()` method acts like the old db_check_record() function, and&lt;br /&gt;
// - keeps the existing value of any keys that are in the ODB and your code&lt;br /&gt;
// - creates any keys that are in your code but not yet in the ODB&lt;br /&gt;
// - deletes any keys that are in the ODB but not your code&lt;br /&gt;
// - updates the order of keys in the ODB to match your code&lt;br /&gt;
o.connect_and_fix_structure(&amp;quot;/Test/Settings&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the ODB path in teh odb::connect() call must start with a &#039;/&#039;. The ODB root path like &amp;lt;code&amp;gt;o.connect(&amp;quot;/&amp;quot;);&amp;lt;/code&amp;gt; is not allowed in this call.&lt;br /&gt;
&lt;br /&gt;
If you want to add new keys to existing ODB subdirectories, you can also just use the [] operator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
existing_key[&amp;quot;MyNewSubKey&amp;quot;] = 1.23;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also create new keys by providing a default value when reading a value. If the key doesn&#039;t already exist, the default value will be used.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb existing_key(&amp;quot;/MyExistingKey&amp;quot;);&lt;br /&gt;
double val = existing_key[&amp;quot;MyNewSubKey&amp;quot;](1.23);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Iterating over subkeys ==&lt;br /&gt;
&lt;br /&gt;
You can iterate over subkeys using normal iterator functions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using standard begin/end.&lt;br /&gt;
midas::odb exp(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
for (auto it = exp.begin(); it != exp.end(); it++) {&lt;br /&gt;
   midas::odb&amp;amp; subkey = *it;&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Iterating using C++11 range-based for loop.&lt;br /&gt;
for (midas::odb&amp;amp; subkey : exp) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; subkey.get_name() &amp;lt;&amp;lt; &amp;quot; = &amp;quot; &amp;lt;&amp;lt; subkey &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can check whether a subkey exists using &amp;lt;code&amp;gt;odb::is_subkey()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Callback functions ==&lt;br /&gt;
&lt;br /&gt;
You may also set up callback functions that are called whenever a value&lt;br /&gt;
changes, using the &amp;lt;code&amp;gt;odb::watch()&amp;lt;/code&amp;gt; function. Note that you must call&lt;br /&gt;
&amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; (from midas.h) periodically for this to work - deep down it&lt;br /&gt;
is &amp;lt;code&amp;gt;cm_yield()&amp;lt;/code&amp;gt; itself that calls your callback function.&lt;br /&gt;
&lt;br /&gt;
The callback functions can either be a &amp;quot;normal&amp;quot; function, a C++ lambda, or a member function of a C++ class.&lt;br /&gt;
In all cases it should accept one argument - a &amp;lt;code&amp;gt;midas::odb&amp;lt;/code&amp;gt; object (passed&lt;br /&gt;
by reference) that contains the new state.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a lambda:&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch([](midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a &amp;quot;normal&amp;quot; function:&lt;br /&gt;
void my_function(midas::odb &amp;amp;arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
to_watch.watch(my_function);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;// Example with a member function of a class:&lt;br /&gt;
#include &amp;lt;functional&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void MyClass::my_function(midas::odb&amp;amp; arg) {&lt;br /&gt;
   std::cout &amp;lt;&amp;lt; &amp;quot;Value of key \&amp;quot;&amp;quot; + arg.get_full_path() + &amp;quot;\&amp;quot; changed to &amp;quot; &amp;lt;&amp;lt; arg &amp;lt;&amp;lt; std::endl;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void MyClass::some_initialisation_code() {&lt;br /&gt;
  // Arguments to std::bind() are: &amp;quot;member function to call&amp;quot;, &amp;quot;object to call that function on&amp;quot;, &amp;quot;placeholder for midas::odb arg&amp;quot;&lt;br /&gt;
  std::function&amp;lt;void(midas::odb&amp;amp;)&amp;gt; callback = std::bind(&amp;amp;MyClass::my_function, this, std::placeholders::_1);&lt;br /&gt;
&lt;br /&gt;
  midas::odb to_watch(&amp;quot;/Experiment&amp;quot;);&lt;br /&gt;
  to_watch.watch(callback);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utility functions ==&lt;br /&gt;
&lt;br /&gt;
There are various utility functions which can be used:&lt;br /&gt;
&lt;br /&gt;
==== void odb::create(const char *name, int type) ====&lt;br /&gt;
&lt;br /&gt;
Simple wrapper around db_create_key() to create a single key in the ODB. &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt; is one of TID_xxx.&lt;br /&gt;
&lt;br /&gt;
==== void odb::delete_key() ====&lt;br /&gt;
&lt;br /&gt;
This member function of a midas::odb object deletes that object from the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;midas::odb o(&amp;quot;/Some/ODB/Path&amp;quot;);&lt;br /&gt;
o.delete_key();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== int odb::delete_key(const std::string &amp;amp;name) ====&lt;br /&gt;
&lt;br /&gt;
This function deletes a key or a subtree in the ODB passed by its path in the &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt; argument. It is a simple wrapper around the C function db_delete_key() and returns the status of that function.&lt;br /&gt;
&lt;br /&gt;
==== bool odb::exists(const std::string *name) ====&lt;br /&gt;
&lt;br /&gt;
This boolean function checks if a key given by its name exists in the ODB.&lt;br /&gt;
&lt;br /&gt;
==== void odb::set_debug(bool flag) / bool odb::get_debug() ====&lt;br /&gt;
&lt;br /&gt;
These functions set and retrieve the debug flag. If the debug flag is &amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt; all communication with the ODB is printed to the screen. This can be helpful in debugging some problems.&lt;br /&gt;
&lt;br /&gt;
== Example code ==&lt;br /&gt;
&lt;br /&gt;
A full working example exploring most of the features can be found in&lt;br /&gt;
&amp;lt;code&amp;gt;odbxx/odbxx_test.cxx&amp;lt;/code&amp;gt;. The test executable will be compiled as&lt;br /&gt;
&amp;lt;code&amp;gt;build/odbxx/odbxx_test&amp;lt;/code&amp;gt; (it is not installed in the `bin` directory).&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3495</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3495"/>
		<updated>2025-04-25T11:02:08Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram || ODB path to X data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram if several histograms are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram if several are used&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-bgcolor || scatter || Color used for &amp;quot;empty&amp;quot; bins (value &amp;lt; 0.5). Mostly used for 2D histograms to set color of empty bins to white&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3494</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3494"/>
		<updated>2025-04-25T09:54:13Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Mouse event function */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram || ODB path to X data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram if several histograms are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram if several are used&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= User-defined tooltip function =&lt;br /&gt;
&lt;br /&gt;
Using the &#039;&#039;&#039;data-tooltip=&amp;quot;func&amp;quot;&#039;&#039;&#039; function, the user might define a custom tooltip generation. The function is called when the mouse hovers over the plot, and passed the current plot object. It then can return a sting for the tooltip to be displayed. Here is an example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have a &#039;data-tooltip=&amp;quot;usertooltip&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
&lt;br /&gt;
   function usertooltip(plotGraph) {&lt;br /&gt;
      let text = &amp;quot;X=&amp;quot; + plotGraph.marker.x + &amp;quot; Y=&amp;quot; + plotGraph.marker.y;&lt;br /&gt;
      if (plotGraph.param.plot[0].type === &amp;quot;colormap&amp;quot;)&lt;br /&gt;
         text += &amp;quot; Z=&amp;quot; + plotGraph.marker.z;&lt;br /&gt;
      return text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3493</id>
		<title>Custom plots with mplot</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_plots_with_mplot&amp;diff=3493"/>
		<updated>2025-04-25T09:49:06Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* Optional parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain custom plots, sich as scatter plots, histograms and color plots. This can be achieved by including the &amp;lt;code&amp;gt;mplot.js&amp;lt;/code&amp;gt; library and creating a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; element. Following example shows the code for a simple page for a scatter plot:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mplot.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);mplot_init()&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 360px;width: 700px;&amp;quot;&lt;br /&gt;
        data-odb-path=&amp;quot;/Path/To/Data&amp;quot;&lt;br /&gt;
        data-x=&amp;quot;X&amp;quot; data-y=&amp;quot;Y&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And here is the resulting page:&lt;br /&gt;
&lt;br /&gt;
[[File:Simple plot.png]]&lt;br /&gt;
&lt;br /&gt;
The data is stored in the ODB under the X-array &amp;lt;code&amp;gt;/Path/To/Data/X&amp;lt;/code&amp;gt; and the Y-array under &amp;lt;code&amp;gt;/Path/To/Data/Y&amp;lt;/code&amp;gt; as two float arrays of the same size.&lt;br /&gt;
&lt;br /&gt;
= Optional parameters =&lt;br /&gt;
&lt;br /&gt;
Several options are possible to modify the plot. Following table gives an overview of the various parameters:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Available parameters for &amp;quot;mplot&amp;quot; div&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Applies to !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || All plot types || Path into the ODB where the data for the plot is stored&lt;br /&gt;
|-&lt;br /&gt;
| data-x || scatter, histogram || ODB path to X data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y || scatter || ODB path to Y data for the first graph (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x1 || scatter, histogram || ODB path to X data for the first graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y1 || scatter || ODB path to Y data for the first graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-x&amp;lt;n&amp;gt; || scatter, histogram || ODB path to X data for the &amp;lt;n&amp;gt;-th graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-y&amp;lt;n&amp;gt; || scatter || ODB path to Y data for the &amp;lt;n&amp;gt;-th graph if several graphs are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h || histogram || ODB path to Y data for a histogram (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-h&amp;lt;n&amp;gt; || histogram || ODB path to Y data for the &amp;lt;n&amp;gt;-th histogram if several histograms are used (relative to data-odb-path)&lt;br /&gt;
|-&lt;br /&gt;
| data-label&amp;lt;n&amp;gt; || scatter, histogram || Label for &amp;lt;n&amp;gt;-th graph or histogram if several are used&lt;br /&gt;
|-&lt;br /&gt;
| data-alpha&amp;lt;n&amp;gt; || scatter, histogram || Transparency (alpha) for graph or histogram if several are used. 0 is completely transparent and 1 is opaque.&lt;br /&gt;
|-&lt;br /&gt;
| data-nx || colormap || Number of X bins for a color map.&lt;br /&gt;
|-&lt;br /&gt;
| data-ny || colormap || Number of Y bins for a color map.&lt;br /&gt;
|- &lt;br /&gt;
| data-z || colormap || ODB path to data for a color map (relative to data-odb-path). If data-nx is 3, and data-ny is 4, then data-z must be 12 elements long (one entry for each x,y co-ordinate). Ordering is &amp;quot;loop through all X indices for the lowest Y index before moving to next Y index&amp;quot;, e.g. (0,0), (1,0), (2,0), (1,0), (1,1) .... (3,0), (3,1), (3,2).&lt;br /&gt;
|-&lt;br /&gt;
| data-title || All plot types || Title of the graph shown on top&lt;br /&gt;
|-&lt;br /&gt;
| data-x-text || All plot types || Label shown below the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-text || All plot types || Label shown left of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-min || scatter, histogram || Minimum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-max || scatter, histogram || Maximum of the X-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-x-log || scatter, histogram || Use logarithmic X-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-y-min || scatter, histogram || Minimum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-max || scatter, histogram || Maximum of the Y-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-y-log || scatter, histogram || Use logarithmic Y-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-z-min || colormap || Minimum of the Z-axis (color axis for color maps)&lt;br /&gt;
|-&lt;br /&gt;
| data-z-max || colormap|| Maximum of the Z-axis&lt;br /&gt;
|-&lt;br /&gt;
| data-z-log || colormap || Use logarithmic Z-axis if &amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| data-overlay || All plot types || Function which gets called after the graph has been drawn. The function can be used to draw an overlay on the graph. See below for an example.&lt;br /&gt;
|-&lt;br /&gt;
| data-event || All plot types || Optional event handler for mouse events. Following parameters are passed to the handler: &amp;quot;event&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;ix&amp;quot; and &amp;quot;iy&amp;quot;. &amp;quot;event&amp;quot; is the original mouse event, &amp;quot;this&amp;quot; is the reference to the mplot object, and the ix/iy are the colum and row numbers if the plot is a 2D colormap. The event handler can the for example look for &amp;quot;dblclick&amp;quot; events and show information about the current bin. If the event handler returns &amp;quot;true&amp;quot;, the framework does not process the mouse event any further. This way one can block the pan/zoom for example.&lt;br /&gt;
|-&lt;br /&gt;
| data-tooltip || All plot types || Optional user function to create tooltip when hovering over the plot. See below for details.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Overlay function =&lt;br /&gt;
&lt;br /&gt;
The overlay function can be used to draw text or graphics on top of the graph. Following function puts a label at the graph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function overlay(plot, ctx) {&lt;br /&gt;
      ctx.fillStyle = &amp;quot;red&amp;quot;;&lt;br /&gt;
      plot.drawTextBox(ctx, &amp;quot;First overlay line\nSecond overlay line&amp;quot;, 120, 150);&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Mouse event function =&lt;br /&gt;
&lt;br /&gt;
The mouse event function can be used to process all types of mouse events. This example shows the current row and column.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // This function will be called if you have &#039;data-event=&amp;quot;mevent&amp;quot;&#039; in your plot&#039;s div:&lt;br /&gt;
   &lt;br /&gt;
   function mevent(event, plot, ix, iy) {&lt;br /&gt;
      if (event.type === &amp;quot;dblclick) {&lt;br /&gt;
         // Note ix and iy are only set for 2D plots; for 1D plots they are undefined&lt;br /&gt;
         console.log(ix + &amp;quot; / &amp;quot; + iy);&lt;br /&gt;
         return true; // don&#039;t process the event any further&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= JSON parameter set =&lt;br /&gt;
&lt;br /&gt;
Instead of defining all parameters with &amp;lt;code&amp;gt;data-xxx&amp;lt;/code&amp;gt; tags, the parameters might be defined inside the &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag using JSON encoding. Following text gives a complete example of all parameters:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mplot&amp;quot; style=&amp;quot;height: 400px;width: 700px;&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
   &amp;quot;showMenuButtons&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;color&amp;quot;: {&lt;br /&gt;
      &amp;quot;background&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;axis&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;label&amp;quot;: &amp;quot;#404040&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;title&amp;quot;: {&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#808080&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 20,&lt;br /&gt;
      &amp;quot;text&amp;quot;: &amp;quot;Customized scatter plot&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;legend&amp;quot;: {&lt;br /&gt;
      &amp;quot;show&amp;quot;: true,&lt;br /&gt;
      &amp;quot;color&amp;quot;: &amp;quot;#D0D0D0&amp;quot;,&lt;br /&gt;
      &amp;quot;backgroundColor&amp;quot;: &amp;quot;#FFFFFF&amp;quot;,&lt;br /&gt;
      &amp;quot;textColor&amp;quot;: &amp;quot;#404040&amp;quot;,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 16&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;xAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;x [mm]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 14&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;yAxis&amp;quot;: {&lt;br /&gt;
      &amp;quot;log&amp;quot;: false,&lt;br /&gt;
      &amp;quot;min&amp;quot;: -10,&lt;br /&gt;
      &amp;quot;max&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;grid&amp;quot;: true,&lt;br /&gt;
      &amp;quot;textSize&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;title&amp;quot;: {&lt;br /&gt;
         &amp;quot;text&amp;quot;: &amp;quot;Scaler [Hz]&amp;quot;,&lt;br /&gt;
         &amp;quot;textSize&amp;quot; : 10&lt;br /&gt;
      }&lt;br /&gt;
   },&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;plot&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;scatter&amp;quot;,&lt;br /&gt;
         &amp;quot;getFromODB&amp;quot;: true,&lt;br /&gt;
         &amp;quot;odbPath&amp;quot;: &amp;quot;/System/Tmp/Plot&amp;quot;,&lt;br /&gt;
         &amp;quot;x&amp;quot;: &amp;quot;X2&amp;quot;,&lt;br /&gt;
         &amp;quot;y&amp;quot;: &amp;quot;Y2&amp;quot;,&lt;br /&gt;
         &amp;quot;label&amp;quot;: &amp;quot;High threshold&amp;quot;,&lt;br /&gt;
         &amp;quot;marker&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: true,&lt;br /&gt;
            &amp;quot;lineColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;fillColor&amp;quot;: 3,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;cross&amp;quot;,&lt;br /&gt;
            &amp;quot;size&amp;quot;: 10,&lt;br /&gt;
            &amp;quot;lineWidth&amp;quot;: 2&lt;br /&gt;
         },&lt;br /&gt;
&lt;br /&gt;
         &amp;quot;line&amp;quot;: {&lt;br /&gt;
            &amp;quot;draw&amp;quot;: false,&lt;br /&gt;
            &amp;quot;fill&amp;quot;: true,&lt;br /&gt;
            &amp;quot;color&amp;quot;: 0,&lt;br /&gt;
            &amp;quot;style&amp;quot;: &amp;quot;solid&amp;quot;,&lt;br /&gt;
            &amp;quot;width&amp;quot;: 1&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
   ]&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The parameter list does not have to be complete. Any existing parameter in this list is combined with the internal default parameter set. The colors might be either a direct color like &amp;quot;red&amp;quot; or &amp;quot;#FF0000&amp;quot;, or an index to the list of 16 internal colors, which have to be chosen to be as far apart as possible in color space.&lt;br /&gt;
&lt;br /&gt;
= More examples =&lt;br /&gt;
&lt;br /&gt;
More plot examples are contained in the &amp;lt;code&amp;gt;plot_example.html&amp;lt;/code&amp;gt; file in the midas/resource/ directory which produces following page:&lt;br /&gt;
&lt;br /&gt;
[[File:Plots.png]]&lt;br /&gt;
&lt;br /&gt;
= Setting parameters and data programmatically =&lt;br /&gt;
&lt;br /&gt;
To set the parameters of a plot via JavaScript code, simply access the plot object and modify its parameters like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  p.param.title.text = &amp;quot;Different titel&amp;quot;;           // change plot title&lt;br /&gt;
  p.param.plot[0].line.color = 2;                   // change line color of first plot to index 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To change the data of the plot, use the function &amp;lt;code&amp;gt;setData(index, x, y)&amp;lt;/code&amp;gt; for scatter plots or &amp;lt;code&amp;gt;setData(index, h)&amp;lt;/code&amp;gt; for histograms like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  let p = document.getElementById(&amp;quot;MyPlot&amp;quot;).mpg;    // obtain plot from ID&lt;br /&gt;
  let x = Array.from({length: 20}, (_, i) =&amp;gt; i);    // create x array with 20 elements 0,1,2,...19&lt;br /&gt;
  let y = x.map(x =&amp;gt; x*x);                          // create y array with y_i = x_i^2&lt;br /&gt;
  p.setData(0, x, y);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To create a plot directly from JavaScript code without a &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mplot&amp;quot; ...&amp;amp;gt;&amp;lt;/code&amp;gt; HTML element, directly use the MPlotGraph() constructor as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;));&lt;br /&gt;
   plot.setData(0, x, y)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A set of parameters can be passed to the constructor. This allows to create plots other than a scatter plot with different parameters such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create colormap plot with parameters&lt;br /&gt;
   let param = {&lt;br /&gt;
      type = &amp;quot;colormap&amp;quot;;&lt;br /&gt;
      showZScale: true,&lt;br /&gt;
      nx: 32,&lt;br /&gt;
      ny: 32,&lt;br /&gt;
      xMin : -20,&lt;br /&gt;
      xMax: 20,&lt;br /&gt;
      yMin: -10,&lt;br /&gt;
      yMax: 10&lt;br /&gt;
   };&lt;br /&gt;
   let plot = new MPlotGraph(document.getElementById(&amp;quot;plotId&amp;quot;), param);&lt;br /&gt;
&lt;br /&gt;
   // create some dummy data&lt;br /&gt;
   let size = 32;&lt;br /&gt;
   let z = Array.from({length: size*size});&lt;br /&gt;
   for (let y=0 ; y&amp;lt;size ; y++)&lt;br /&gt;
      for (let x=0 ; x&amp;lt;size ; x++) {&lt;br /&gt;
         z[y*size + x] = 10 * Math.exp(-(x-size/2)*(x-size/2) / size) *&lt;br /&gt;
            Math.exp(-(y-size/2)*(y-size/2) / size );&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
   // display dummy data&lt;br /&gt;
   plot.setData(0, z)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3472</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3472"/>
		<updated>2025-02-26T09:03:07Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* modbgauge */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.&lt;br /&gt;
&lt;br /&gt;
We note that MIDAS has provided a number of different ways of providing custom pages over the years.  A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a &amp;quot;virtual&amp;quot; X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.&lt;br /&gt;
&lt;br /&gt;
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.&lt;br /&gt;
&lt;br /&gt;
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in&lt;br /&gt;
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: Without the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.&lt;br /&gt;
&lt;br /&gt;
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions.  The advantages of using these modb* javascript functions are&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient.  In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call).  It will also require more coding to implement the custom page with only MjsonRPC calls.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 1  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3471</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3471"/>
		<updated>2025-02-26T09:02:43Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* modbthermo */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.&lt;br /&gt;
&lt;br /&gt;
We note that MIDAS has provided a number of different ways of providing custom pages over the years.  A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a &amp;quot;virtual&amp;quot; X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.&lt;br /&gt;
&lt;br /&gt;
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.&lt;br /&gt;
&lt;br /&gt;
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in&lt;br /&gt;
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: Without the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.&lt;br /&gt;
&lt;br /&gt;
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions.  The advantages of using these modb* javascript functions are&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient.  In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call).  It will also require more coding to implement the custom page with only MjsonRPC calls.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 1  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3470</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3470"/>
		<updated>2025-02-26T09:02:05Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* modbhbar */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.&lt;br /&gt;
&lt;br /&gt;
We note that MIDAS has provided a number of different ways of providing custom pages over the years.  A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a &amp;quot;virtual&amp;quot; X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.&lt;br /&gt;
&lt;br /&gt;
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.&lt;br /&gt;
&lt;br /&gt;
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in&lt;br /&gt;
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: Without the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.&lt;br /&gt;
&lt;br /&gt;
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions.  The advantages of using these modb* javascript functions are&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient.  In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call).  It will also require more coding to implement the custom page with only MjsonRPC calls.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 1  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
	<entry>
		<id>https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3469</id>
		<title>Custom Page</title>
		<link rel="alternate" type="text/html" href="https://daq00.triumf.ca/MidasWiki/index.php?title=Custom_Page&amp;diff=3469"/>
		<updated>2025-02-26T09:01:44Z</updated>

		<summary type="html">&lt;p&gt;Stefan Ritt: /* modbhbar */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Pagelinks}}&lt;br /&gt;
&lt;br /&gt;
= Links =&lt;br /&gt;
{{mhttpdpages3|[[Custom Page Features]]|[[/Custom ODB tree]]|[[Mhttpd.js|MIDAS Javascript library]]}}&lt;br /&gt;
&lt;br /&gt;
= Purpose =&lt;br /&gt;
A user-created [[mhttpd]] Custom Web Page accessible from the side menu allows the user additional flexibility. For example, a custom page may present the essential parameters of the controlled experiment in a more compact way. A custom page may even replace the default [[Status Page]].&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Custom web pages provide the user with a means of creating secondary user-created web page(s) activated within the standard MIDAS web interface. These custom pages usually display ODB parameters or data to the user. They can contain specific links to the ODB so the user may also input information relevant to the experiment. Users create Custom Pages when the standard pages do not meet their requirements completely.&lt;br /&gt;
&lt;br /&gt;
We note that MIDAS has provided a number of different ways of providing custom pages over the years.  A new scheme of custom pages making use of modern HTML5 techniques has been introduced in 2017. This page will mostly only be providing documentation for the new scheme of custom pages.&lt;br /&gt;
&lt;br /&gt;
= Examples of Custom Pages =&lt;br /&gt;
&lt;br /&gt;
Click on the thumbnails to enlarge.&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Capture_sgas.png|thumb|left|Figure 1: MEG Gas System]] || &#039;&#039;&#039;Example 1&#039;&#039;&#039;&lt;br /&gt;
This page (Figure 1) from the MEG experiment at PSI shows a complex gas system. This shows the use of &amp;quot;fills&amp;quot; and &amp;quot;labels&amp;quot;. Open valves are represented as green circles, closed valves as red circles. If, for example, an open valve is clicked, the valve closes, and the circle turns red (provided the user successfully supplied the correct password).&lt;br /&gt;
|-&lt;br /&gt;
| [[File:custom_ROOT_analyzer_page.png|thumb|left|Figure 2: ROOT Analyzer (MEG Experiment)]] || &#039;&#039;&#039;Example 2&#039;&#039;&#039;&lt;br /&gt;
Many MIDAS experiments work with ROOT based analyzers today. One problem is that the graphical output of the root analyzer can only be seen through the X server and not through the web. At the MEG experiment, this problem was solved in an elegant way: The ROOT analyzer runs in the background, using a &amp;quot;virtual&amp;quot; X server called Xvfb. It plots its output (several panels) normally using this X server, then saves this panels every ten seconds into GIF files. These GIF files are then served through mhttpd using a custom page. The output is shown in Figure 2.&lt;br /&gt;
&lt;br /&gt;
The buttons on the left sides are actually HTML buttons on that custom page overlaid to the GIF image, which in this case shows one of the 800 PMT channels digitized at 1.6 GSPS. With these buttons one can cycle through the different GIF images, which then automatically update ever ten seconds. Of course it is not possible to feed interaction back to the analyzer (i.e. the waveform cannot be fitted interactively) but for monitoring an experiment in production mode this tool is extremely helpful, since it is seamlessly integrated into mhttpd. All the magic is done with JavaScript, and the buttons are overlaid on the graphics using CSS with absolute positioning. The analysis ratio on the top right is also done with JavaScript accessing the required information from the ODB. &lt;br /&gt;
&lt;br /&gt;
For details using Xvfb server, please contact Ryu Sawada &amp;lt;sawada@icepp.s.u-tokyo.ac.jp&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| [[File:deap_custom_scb.png|thumb|left|Figure 3: SCB Setup (Deap Experiment)]] || &#039;&#039;&#039;Example 3&#039;&#039;&#039;&lt;br /&gt;
This custom page from the Deap Experiment (Figure 3) allows the users to easily set individual channels, or a group of channels, or all channels of the SCB modules to a particular value.   &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Access a Custom Page from the Regular MIDAS pages =&lt;br /&gt;
Access to a Custom Page is set up through the [[/Custom ODB tree]] (see [[/Custom ODB tree#Keys in the /Custom tree|custom-link]]). This associates a custom page file on the disk with a menu item on the left navigation bar. Clicking on the resulting link will display that custom page.&lt;br /&gt;
&lt;br /&gt;
Often a custom page requires resources such as *.css (stylesheets) or *.js (javascript) files. It is convenient to store all such files with the custom page file (*.html) in&lt;br /&gt;
a particular directory, e.g. /home/expt/online/custom. By creating an ODB key /Custom/Path, all the custom page files and resources can be served easily from this directory.&lt;br /&gt;
See [[Custom Page Features#Resource files]] for more information.&lt;br /&gt;
&lt;br /&gt;
If the key  {{Odbpath|path=/Custom/myPage&amp;amp;}}  (see Note) is created, e.g.&lt;br /&gt;
 odbedit&amp;gt; ls /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
&lt;br /&gt;
the custom link on the left navigation bar will be &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; and the URL for the resulting custom page will be of the form &amp;lt;code&amp;gt;http://myhost.mydomain:myport/cmd=?Custom&amp;amp;page=myPage&amp;lt;/code&amp;gt; (see also [[mhttpd#usage]]).  &lt;br /&gt;
Clicking on &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; will display the custom page in the same window.&lt;br /&gt;
&lt;br /&gt;
;Note&lt;br /&gt;
: Without the &amp;quot;&amp;amp;&amp;quot; symbol in the key name, the page would appear in a new window. See [[/Custom ODB tree#Key names|Key names]] for more information.&lt;br /&gt;
&lt;br /&gt;
If an experiment used many custom pages, the menu on the left side can get pretty long. To avoid that, custom pages can be structured in submenus. Simply put a custom page in an ODB subdirectory, and it will appear in a separate submenu, e.g.&lt;br /&gt;
&lt;br /&gt;
 odbedit&amp;gt; ls -r /Custom&lt;br /&gt;
    Path                              /home/expt/online/custom&lt;br /&gt;
    myPage&amp;amp;                           mypage.html&lt;br /&gt;
    Calorimeter&lt;br /&gt;
        HV                            hv.html&lt;br /&gt;
        Rates                         rates.html&lt;br /&gt;
    Baeam&lt;br /&gt;
        Beamline                      beamline.html&lt;br /&gt;
        Accelerator                   accel.html&lt;br /&gt;
&lt;br /&gt;
The pages then include the subdirectory in the URL, like &lt;br /&gt;
&lt;br /&gt;
 http://localhost:8080?cmd=custom&amp;amp;page=beam/Beamline&lt;br /&gt;
&lt;br /&gt;
Subdirectories can contain nested subdirectories. Please make sure that you specify the full path in your mhttpd_init() call, such as&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Calorimeter/HV&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in order to keep the submenu open after you select the custom page.&lt;br /&gt;
&lt;br /&gt;
= How to write a custom page =&lt;br /&gt;
A custom page is usually written in a combination of HTML and Javascript. It can contain any of the features described below. A [[Mjsonrpc | Javascript MjsonRPC Library]] has been written to provide access to the ODB and other functions.&lt;br /&gt;
&lt;br /&gt;
In what follows, we describe a scheme for writing custom pages with the set of modb* javascript functions.  The advantages of using these modb* javascript functions are&lt;br /&gt;
&lt;br /&gt;
* modb* functions hide details about the underlying MjsonRPC calls, which should allow a user to write pretty and sophisticated custom pages quickly and cleanly.&lt;br /&gt;
* modb* functions ensure that all the periodic updates of the ODB value (and other MIDAS information) are done in a single MjsonRPC batch call, which should ensure optimal page loading speed.&lt;br /&gt;
* modb* functions encapsulate the underlying communication. Should the communication change in the future, the custom pages do not have to be changed.&lt;br /&gt;
&lt;br /&gt;
It is also possible for users to write their custom pages using only the underlying [[Mjsonrpc | MjsonRPC library]] calls, but they then need to ensure on their own that the page loading remains efficient.  In particular, if you combine the standard MIDAS navigation bars (described in next section) with your own periodic MjsonRPC calls then you will probably need at least two separate periodic RPC calls to populate the custom page (instead of one call).  It will also require more coding to implement the custom page with only MjsonRPC calls.&lt;br /&gt;
&lt;br /&gt;
== How to use the standard MIDAS navigation bars on your custom page == &lt;br /&gt;
&lt;br /&gt;
If you want to have your custom page use the same header and navigation bars as the standard MIDAS pages, you need to use the following syntax &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;myPage&amp;lt;/title&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;myPage&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_start --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
 ADD YOUR HTML/JS  CODE here...&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The call &amp;lt;code&amp;gt;mhttpd_init(&#039;myPage&#039;)&amp;lt;/code&amp;gt; is executed when the page is loaded, and  &amp;lt;code&amp;gt;myPage&amp;lt;/code&amp;gt; is the name of the page shown on the left menu bar. This corresponds to an ODB entry /Custom/myPage.&lt;br /&gt;
This pattern will allow you to use the standard MIDAS navigation whether you are using the modb* functions or the underlying [[Mjsonrpc | javascript libraries]].&lt;br /&gt;
&lt;br /&gt;
= modb* Javascript scheme = &lt;br /&gt;
&lt;br /&gt;
The general scheme of the custom page scheme is to write &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;&amp;amp;lt;span class=&amp;quot;modb...&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/code&amp;gt; tags with special class names, most of them starting with &amp;quot;modb...&amp;quot; (&amp;quot;MIDAS-ODB&amp;quot;). Use a &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want the element to appear in a separate line, and use the &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag if you want to display the element in-line. The following description uses only &amp;lt;code&amp;gt;&amp;amp;lt;div&amp;amp;gt;&amp;lt;/code&amp;gt; tags, but all of them can be changed to &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
All HTML tags with &amp;quot;modb...&amp;quot; names are scanned by the &amp;lt;code&amp;gt;mhttp_init(&#039;name&#039;)&amp;lt;/code&amp;gt; function upon page load, and their inner contents are replaced by the requested ODB value or some graphics. The contents are then updated regularly. Updates are once per second by default. This can be changed by passing a second argument to &amp;lt;code&amp;gt;mhttpd_init(&#039;name&#039;, interval)&amp;lt;/code&amp;gt; where &amp;quot;interval&amp;quot; is in milliseconds. You can add or remove &amp;quot;modb...&amp;quot; elements at any time using javascript, and the new elements will be &amp;quot;discovered&amp;quot; automatically during the next update.&lt;br /&gt;
&lt;br /&gt;
== modbset(path, value) ==&lt;br /&gt;
&lt;br /&gt;
To set values in the ODB, the midas JavaScript function mjsonrpc_db_paste() is usually called. This function is implemented as a JavaScript promise, which lets you chain several request in order to change values inside the ODB in a certain order. If that functionality is not required, the simplified modbset() function can be called, which also implements standard error handling. Two versions of this function exist, one which accepts a single ODB path and a single value, and one which accepts an array of ODB paths and values:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset(&amp;quot;odb path&amp;quot;, value)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
modbset([&amp;quot;odb path1&amp;quot;, &amp;quot;odb path2&amp;quot;, ...], [value1, value2, ...])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These functions are typically used by custom JavaScript code, like when some value in an experiment exceeds some limit and some action has to be taken like to close a valve. If the call fails (like if mhttpd is dead), a window with an error description is shown.&lt;br /&gt;
&lt;br /&gt;
== modb ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for Midas ODB) &amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot; onchange=&amp;quot;func()&amp;quot;&amp;amp;gt;&amp;lt;/code&amp;gt; can be used to call a user-defined function func() if a value in the ODB changes. This function must be defined inline or in a separate &amp;amp;lt;script&amp;amp;gt;...&amp;amp;lt;/script&amp;amp;gt; section, and can execute any operation, such as opening a dialog box, hiding/unhiding parts of the custom page, or changing colors and styles of page elements.&lt;br /&gt;
&lt;br /&gt;
The current value of the ODB entry is available inside the &amp;quot;onchange&amp;quot; function as &#039;&#039;&#039;this.value&#039;&#039;&#039;. Following tag will call a function which logs the current run number in the JavaScript console window:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB path does not point to an individual value but to a subdirectory, the whole subdirectory is mapped to &#039;&#039;&#039;this.value&#039;&#039;&#039; as a JavaSctipt object such as&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Runinfo&amp;quot; onchange=&amp;quot;func(this.value)&amp;quot;&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;amp;lt;script&amp;amp;gt;function func(value) {&lt;br /&gt;
console.log(value[&amp;quot;run number&amp;quot;]);&lt;br /&gt;
}&amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Note that ODB entries are mapped to JavaScript objects without change. So if an ODB entry name contains a blank, it must be accessed via the JS square bracket &#039;&#039;&#039;value[&amp;quot;run number&amp;quot;]&#039;&#039;&#039; as shown in the above example. Otherwise, the entry can be accessed via the dot notation, such as &#039;&#039;&#039;value.state&#039;&#039;&#039; for /Runinfo/State for example.&lt;br /&gt;
&lt;br /&gt;
== modbvalue ==&lt;br /&gt;
&lt;br /&gt;
This special HTML div tag (abbreviation stands for &amp;quot;Midas ODB VALUE&amp;quot;) &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
is now automatically replaced by the value in the ODB found at the given path and updated regularly as described above Following options are valid for this tag:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1: List of valid options for modbvalue tag&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| data-name || class=&amp;quot;modbvalue&amp;quot; || Tells the framework to replace this tag with an ODB value&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || data-odb-path = &amp;quot;/Runinfo/Run number&amp;quot; || Path to the value in the ODB. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]].&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-editable || data-odb-editable=&amp;quot;1&amp;quot; || If set, the value is not only shown, but is also clickable for in-line editing. Hitting return send the new value to the ODB.&lt;br /&gt;
|-&lt;br /&gt;
| data-format || data-format=&amp;quot;f3&amp;quot; || Specify format of data shown. See Table 2 below for options.&lt;br /&gt;
|-&lt;br /&gt;
| data-size || data-size=&amp;quot;8&amp;quot; || Specify size (in chars) of edit box if one modifies the value. Default is 10.&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || data-formula=&amp;quot;2*x+3&amp;quot; || Specify an optional formula to process with the current ODB value stored in x&lt;br /&gt;
|-&lt;br /&gt;
| data-validate || data-validate=&amp;quot;func&amp;quot; || Specify an optional validation function which gets called before submitting data (see below)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Validation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, an optional validation function can be called via the &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; option. The function&lt;br /&gt;
will be called with the current value and a reference to the current modbvalue element. If the function returns &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;, then the value&lt;br /&gt;
is not sent to the ODB.&lt;br /&gt;
&lt;br /&gt;
Following example shows a function which just rejects the submission of values above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;amp;gt;&amp;amp;lt;/script&amp;amp;gt; &amp;amp;lt;!-- needed of dlgAlert() --&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        dlgAlert(&amp;quot;Value cannot be above 1000&amp;quot;);&lt;br /&gt;
        return false;&lt;br /&gt;
     }&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following function corrects the return value to 1000 if it&#039;s above 1000:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;script&amp;amp;gt;&lt;br /&gt;
  function my_validate2(value, element) {&lt;br /&gt;
     if (value &amp;gt; 1000) {&lt;br /&gt;
        element.childNodes[0].value = 1000;&lt;br /&gt;
     return true;&lt;br /&gt;
  }&lt;br /&gt;
  &amp;amp;lt;/script&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Confirmation ===&lt;br /&gt;
&lt;br /&gt;
Before a modified value is submitted to the ODB, a confirmation dialog box can be displayed to let the user confirm the change before it is actually written to the ODB. &lt;br /&gt;
This is done with the option &amp;lt;code&amp;gt;data-confirm=&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt;. A dialog box is then shown with the &amp;lt;code&amp;gt;&amp;amp;lt;string&amp;amp;gt;&amp;lt;/code&amp;gt; as the main text and two buttons with &amp;quot;OK&amp;quot; and &amp;quot;Cancel&amp;quot;.&lt;br /&gt;
Hitting &amp;quot;OK&amp;quot; finally writes the value to the ODB, hitting &amp;quot;Cancel&amp;quot; keeps the old value.&lt;br /&gt;
&lt;br /&gt;
Following example shows an example:&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;div class=&amp;quot;modbvalue&amp;quot; ... data-odb-editable=&amp;quot;1&amp;quot; data-confirm=&amp;quot;Are you sure to change the value?&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Following dialog box is then showed:&lt;br /&gt;
&lt;br /&gt;
[[File:Confirm.png|frame|left|Optional confirm dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Formatting ===&lt;br /&gt;
&lt;br /&gt;
Table 2 below lists the format specifiers supported with a modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 2: Format specifiers for modbvalue tag &amp;lt;code&amp;gt;data-format=&amp;quot;...&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
! Option !! Valid for* !! Example !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| %d || int || 1234 || Shows a number in decimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %t || int, float || 1,234,567 || Shows a number in decimal encoding with commas as thousands separator&lt;br /&gt;
|-&lt;br /&gt;
| %x || int || 0x4D2 || Shows a number in hexadecimal encoding&lt;br /&gt;
|-&lt;br /&gt;
| %b || int || 10011010010b|| Shows a number in binary encoding. Options t, d, x, b can be combined, like &amp;lt;code&amp;gt;data-format=&amp;quot;%d / %x&amp;quot;&amp;lt;/code&amp;gt; to produce &amp;lt;code&amp;gt;1234 / 0x4D2&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| %f&amp;amp;lt;x&amp;amp;gt; || float || 1.234 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal. A value of f0 shows only the integer part.&lt;br /&gt;
|-&lt;br /&gt;
| %p&amp;amp;lt;x&amp;amp;gt; || float || 1.23 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; significant digits of precision, independent of the decimal. For example a value of p2 can render a number to 12000 or 0.0012&lt;br /&gt;
|-&lt;br /&gt;
| %e&amp;amp;lt;x&amp;amp;gt; || float || 1.23e-05 || Shows a floating point number with &amp;amp;lt;x&amp;amp;gt; digits after the decimal in exponential format. Useful for small number such as pressures.&lt;br /&gt;
|-&lt;br /&gt;
| T= %f1 C || float || T= 25.4 C || Combination of text and float value is possible.&lt;br /&gt;
|-&lt;br /&gt;
| %d / %x / %b || int || 17 / 0x11 / 10001b|| Same integer value in different formats&lt;br /&gt;
|-&lt;br /&gt;
| [col1,col2] || bool || [red,var(--mgreen)] || Generates a color box filled with col1 (false, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: red&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;) or col2 (true, &amp;lt;span style=&amp;quot;display: inline-block; width: 1em; height: 1em; border: 1px solid black; background-color: #a0ffb8&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Note that valid for &amp;quot;int&amp;quot; means all integral ODB types (regardless of size or signed/unsigned) and valid for &amp;quot;float&amp;quot; means both float and double.&lt;br /&gt;
&lt;br /&gt;
== modbbutton ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a push-button which can set a certain ODB entry to a specific value. To set the &amp;quot;Run number&amp;quot; to 100, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;100&amp;quot; data-validate=&amp;quot;my_validate&amp;quot;&amp;amp;gt;[Button Text]&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent pressing the button under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for modbvalue tag.&lt;br /&gt;
&lt;br /&gt;
== modbbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a rectangular box which changes color according to a value in the ODB. If the value is nonzero or true (for booleans), the &#039;&#039;&#039;data-color&#039;&#039;&#039; is used, otherwise the &#039;&#039;&#039;data-background-color&#039;&#039;&#039; is used&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write Data&amp;quot; data-formula=&amp;quot;x &amp;gt; 0&amp;quot; style=&amp;quot;width: 30px; height: 30px; border: 1px solid black&amp;quot; data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optionally, a &amp;lt;code&amp;gt;data-formula&amp;lt;/code&amp;gt; can be specified. The formula sees the ODB value in the variable &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, and can do any boolean operation. If the result of this is true, then the box gets the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt;, otherwise the &amp;lt;code&amp;gt;data-background-color&amp;lt;/code&amp;gt;.&lt;br /&gt;
Examples for these formulas are &amp;lt;code&amp;gt;x &amp;gt; 10&amp;lt;/code&amp;gt; for a comparison or &amp;lt;code&amp;gt;x &amp;amp; 1&amp;lt;/code&amp;gt; which will do a bitwise AND operation and is true only for odd numbers.&lt;br /&gt;
&lt;br /&gt;
== modbcheckbox ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a check box which can set a certain ODB entry to true or false. To set the &amp;quot;Write data&amp;quot; flag for the logger true or false, one can use the following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; data-validate=&amp;quot;my_validate&amp;quot; /&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
If the ODB value changed by this control is of type integer, its value will be set to 1 or 0. The optional &amp;lt;code&amp;gt;data-validate&amp;lt;/code&amp;gt; function can be used to prevent changing a value under certain circumstances. If the validate function returns false, the value is not written to the ODB, similarly than for &amp;lt;code&amp;gt;modbvalue&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
== modbselect ==&lt;br /&gt;
&lt;br /&gt;
This tag generates a drop down box with certain valued which can be sent to a value in the ODB. Following example shows a drop-down box with three different values. When selected, they are sent to the ODB under &amp;lt;code&amp;gt;/Runinfo/Run number&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;1&amp;quot;&amp;amp;gt;1&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;5&amp;quot;&amp;amp;gt;5&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;option value=&amp;quot;10&amp;quot;&amp;amp;gt;10&amp;amp;lt;/option&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of specifying the valid options in the javascript code, you can also specify them in the ODB, and populate the drop-down with those values. Specify &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; to enable this behaviour. For ODB key &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, you will need to create an ODB entry called &amp;lt;code&amp;gt;Options x&amp;lt;/code&amp;gt; in the same directory; this &amp;quot;options&amp;quot; key should be a list, with each element in the list being an allowable option.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;!-- Will read options from &amp;quot;/Equipment/Example/Settings/Options Something&amp;quot; in this case --&amp;gt;&lt;br /&gt;
  &amp;amp;lt;select class=&amp;quot;modbselect&amp;quot; data-odb-path=&amp;quot;/Equipment/Example/Settings/Something&amp;quot; data-auto-options=&amp;quot;1&amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/select&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
The benefit of the &amp;lt;code&amp;gt;data-auto-options=&amp;quot;1&amp;quot;&amp;lt;/code&amp;gt; approach is that the same options will be shown on the regular ODB browser webpage. &lt;br /&gt;
&lt;br /&gt;
Note that these options are only a convenience for the user interface - there is no strict enforcement in the ODB itself! Power-users are able to set other values via C++/Python/Javascript/odbedit etc.&lt;br /&gt;
&lt;br /&gt;
== modbhbar ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px; color: lightgreen;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal bar with a total length of 500px. Depending on the ODB value {{Odbpath|path=Run number}}. If {{Odbpath|path=Run number}} is 10, then the bar is filled all the way to the right, if {{Odbpath|path=Run number}} is 5, the bar is only filled halfway. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;color: red&amp;quot; || Color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;background-color: red&amp;quot; || Background color of horizontal bar || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed. If omitted, the value must be set via [[Custom_Page#Changing_values_of_indicators_programmatically | set_value]]. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of bar range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of bar range || 1 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown as text overlay || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specify format of data shown. See Table 2 above for options || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== mhaxis ==&lt;br /&gt;
&lt;br /&gt;
A horizontal bar can be combined with an axis with tick marks and labels. The axis can be above or below the bar.&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width: 500px; height: 22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a horizontal axis next to the bar. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 500px&amp;quot; || Total width of the axis, must match the width of the horizontal bar || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 18px&amp;quot; || Height of the axis, must be enough to display labels  || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align: top&amp;quot; || Must be &amp;quot;top&amp;quot; if the axis is below the bar || &amp;quot;bottom&amp;quot; if not given&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Left limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Right limit of axis range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-log || Logarithmic display || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbvbar ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;modbhbar&amp;lt;/code&amp;gt;, except the bar grows vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== mvaxis ==&lt;br /&gt;
&lt;br /&gt;
Same as &amp;lt;code&amp;gt;mhaxis&amp;lt;/code&amp;gt;, except the axis is shown vertically instead of horizontally.&lt;br /&gt;
&lt;br /&gt;
== modbthermo ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a vertical thermometer ranging from -10 to 30. Depending on the ODB value {{Odbpath|path=Run number}}. The run number was chosen instead of a real temperature since this ODB variable exists in all midas installations by default, so it&#039;s good for testing. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 30px&amp;quot; || Width of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 100px&amp;quot; || Total height of the thermometer || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of thermometer || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of thermometer background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown next to the thermometer || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== modbgauge ==&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width: 100px; height: 50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-color=&amp;quot;darkgreen&amp;quot;&amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
shows a circular gauge ranging from 0 to 10. Depending on the ODB value &amp;quot;Run number&amp;quot;. Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 100px&amp;quot; || Width of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 50px&amp;quot; || Total height of the gauge || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-odb-path || ODB path of value being displayed || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-max-value || Upper range || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-min-value || Lower range || 0 if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-color || Color of gauge || Black if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-background-color || Color of gauge background || Transparent if not present&lt;br /&gt;
|-&lt;br /&gt;
| data-print-value || If &amp;quot;1&amp;quot;, data value is shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-format || Specifies format of temperature shown below gauge. See Table 2 for options. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || If &amp;quot;1&amp;quot;, the min and max values of the range are shown below the gauge || No&lt;br /&gt;
|-&lt;br /&gt;
| data-formula || Specify an optional formula to process with the current ODB value stored in x || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the gauge scale is not shown, the gauge height should be half the gauge width. If the scale is shown, 15px must be added to the height.&lt;br /&gt;
&lt;br /&gt;
== Changing properties of controls dynamically ==&lt;br /&gt;
&lt;br /&gt;
All custom controls can be configured to call a user&#039;s function when the control is first set up, or when the value changes. This is done by specifying the &#039;&#039;&#039;onload&#039;&#039;&#039; and/or &#039;&#039;&#039;onchange&#039;&#039;&#039; functions.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;onload&#039;&#039;&#039; is called only once, when the control&#039;s value is first read from the ODB.&lt;br /&gt;
* &#039;&#039;&#039;onchange&#039;&#039;&#039; is called each time the value in the ODB changes.&lt;br /&gt;
&lt;br /&gt;
The onload/onchange functions have access to the current value and can change any of the parameters of the control. The following callback for example changes the color of a thermometer to red if the value is above 30 and to blue if it is below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 30?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
onchange can call any arbitrary javascript function. Rather than specifying the logic in the tag itself, the above example could also be implemented like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
function check_therm(elem) {&lt;br /&gt;
  if (elem.value &amp;gt; 30) {&lt;br /&gt;
    elem.dataset.color = &amp;quot;red&amp;quot;;&lt;br /&gt;
  } else {&lt;br /&gt;
    elem.dataset.color = &amp;quot;blue&amp;quot;;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width: 30px; height: 100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot; data-color=&amp;quot;blue&amp;quot; data-print-value=&amp;quot;1&amp;quot; onchange=&amp;quot;check_therm(this)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Other example use cases include showing/hiding other elements on a webpage based on whether an modbcheckbox is checked or not, or temporarily changing the background color of an element to highlight that a value has changed.&lt;br /&gt;
&lt;br /&gt;
If you want the same function to be called for both onload and onchange, you could set &amp;lt;code&amp;gt;onload=&amp;quot;onchange()&amp;quot;&amp;lt;/code&amp;gt; and have the &amp;quot;real&amp;quot; function in onchange.&lt;br /&gt;
&lt;br /&gt;
== Changing values of indicators programmatically ==&lt;br /&gt;
&lt;br /&gt;
Usually, custom controls are directly linked to values in the ODB. Sometimes it is however necessary to combine several ODB values into a single indicator, like if you want to show the difference of two ODB values. &lt;br /&gt;
&lt;br /&gt;
For such cases, the &amp;lt;code&amp;gt;data-odb-path&amp;lt;/code&amp;gt; attribute can be removed and the the display value can be changed via JavaScript code like following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mythermo&amp;quot; class=&amp;quot;modbthermo&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
  let t = document.getElementById(&amp;quot;mythermo&amp;quot;);&lt;br /&gt;
  t.setValue(15);&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mjshistory =&lt;br /&gt;
&lt;br /&gt;
Custom pages can contain one or more specific history panels usually shown on the &amp;quot;History&amp;quot; page. This makes it easy to combine current readings of values together with the history of these values.&lt;br /&gt;
&lt;br /&gt;
To enable interactive history panels, following lines have to be added to your custom page:&lt;br /&gt;
&lt;br /&gt;
Inisde the &amp;lt;head&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the &amp;lt;body&amp;gt; tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;body ... onload=&amp;quot;mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following tag:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;&amp;lt;group&amp;gt;&amp;quot; data-panel=&amp;quot;&amp;lt;panel&amp;gt;&amp;quot; style=&amp;quot;width: 320px; height: 200px;&amp;quot; &amp;amp;gt;&amp;amp;lt;/div&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
shows a history panel defined in the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; (replace &amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt; with groups and panels from your experiment).&lt;br /&gt;
&lt;br /&gt;
Following options are possible:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Setting !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| data-group || ODB group of history. Has to match a group under /History/Display || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-panel || ODB panel name of history. Has to match a panel name under /History/Display/&amp;amp;lt;group&amp;amp;gt;/ || Yes&lt;br /&gt;
|-&lt;br /&gt;
| data-scale || Time scale of history plot. Use 10m for 10 minutes and 5h for 5 hours. If not specified, the value from the ODB under /History/Display/&amp;amp;lt;group&amp;amp;gt;/&amp;amp;lt;panel&amp;amp;gt;/Timescale is used. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-title || Flag to show or hide the title. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-values || Flag to show or hide the variable labels with their current value. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-axis || Flag to show or hide the X/Y axis. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the menu buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| data-show-menu-buttons || Flag to show or hide the zoom buttons. Default is &amp;quot;1&amp;quot;. || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;width: 320px&amp;quot; || Width of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;height: 200px&amp;quot; || Height of the history panel || No&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border: 1px solid black&amp;quot; || Border around the history panel || No&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If width and height are omitted, the default values of 320px and 200px are used. History panels are automatically updated every second.&lt;br /&gt;
&lt;br /&gt;
In addition, it is possible to show a floating dialog box with a history panel. That might be useful if you show a single value on a custom page, and want to give users &lt;br /&gt;
the possibility to show the history of that variable. Just put a button next to the value and call &#039;&#039;&#039;mhistory_dialog(&amp;amp;lt;group&amp;amp;gt;, &amp;amp;lt;panel&amp;amp;gt;)&#039;&#039;&#039; from that button like:&lt;br /&gt;
&lt;br /&gt;
   &amp;amp;lt;span class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Some/Path&amp;quot;&amp;amp;gt;&amp;amp;lt;/span&amp;amp;gt;&lt;br /&gt;
   &amp;lt;button onclick=&amp;quot;mhistory_dialog(&#039;group&#039;,&#039;panel&#039;)&amp;quot;&amp;gt;&amp;lt;img src=&amp;quot;icons/activity.svg&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The history panel will then be opened when the user clicks the button:&lt;br /&gt;
&lt;br /&gt;
[[File:History dialog.png|frame|left|Floating history dialog]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If one wants to avoid the definition of a history panel in the ODB, a &amp;quot;direct variable plot&amp;quot; can be done with the function &#039;&#039;&#039;mhistory_dialog_var(&amp;amp;lt;variable&amp;amp;gt;)&#039;&#039;&#039; where &amp;amp;lt;variable&amp;amp;gt; has the format like the variable definition in the ODB (e.g. &amp;quot;System:Trigger per sec.&amp;quot;). Such a plot has some default parameters for the timescale etc., which can be overwritten by passing a parameter object to the function such as &#039;&#039;&#039;mhistory_dialog_var(&amp;quot;System:Trigger per sec.&amp;quot;, {&amp;quot;Timescale&amp;quot;: &amp;quot;24h&amp;quot;});&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A full example of a custom page with a history panel is shown below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
  &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;script src=&amp;quot;mhistory.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;mhttpd_init(&#039;history_example&#039;); mhistory_init();&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;
  &amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;mjshistory&amp;quot; data-group=&amp;quot;EPICS&amp;quot; data-panel=&amp;quot;Logging&amp;quot; style=&amp;quot;width: 500px; height: 300px;&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= mplot =&lt;br /&gt;
&lt;br /&gt;
Custom pages may contain scatter plots, histograms and other graphics. The syntax is described on the dedicated [[Custom plots with mplot]] page.&lt;br /&gt;
&lt;br /&gt;
= Dialog, popup and modal boxes =&lt;br /&gt;
&lt;br /&gt;
You can spawn a dialog box to either show a message to the user, ask for input, or require confirmation of an action. You can call all these functions from your own javascript code.&lt;br /&gt;
&lt;br /&gt;
== Showing a message ==&lt;br /&gt;
&lt;br /&gt;
The dlgMessage, dlgAlert and dlgWait functions show a message to the user. The dialog box contains an &amp;quot;Ok&amp;quot; button that the user may click to dismiss the message.&lt;br /&gt;
&lt;br /&gt;
=== dlgMessage ===&lt;br /&gt;
&lt;br /&gt;
dlgMessage is the most customisable of the 3 functions. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(title, message, modal, error, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| title || string || Title of the dialog box || Yes&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || Main message content || Yes&lt;br /&gt;
|-&lt;br /&gt;
| modal || boolean || If true, will grey out the rest of the page and require the user to dismiss the alert box before they can continue interacting with the page. || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| error || boolean || If true, will use a red background for the title || No. Defaults to false&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called with the user clicks the &amp;quot;Ok&amp;quot; button. The callback function will be called with just a single paramter. || No. Defaults to not calling a callback function.&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
 &lt;br /&gt;
Some example invocations are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgMessage(&amp;quot;Message title&amp;quot;, &amp;quot;Message text&amp;quot;);&lt;br /&gt;
dlgMessage(&amp;quot;It&#039;s an error!&amp;quot;, &amp;quot;&amp;lt;b&amp;gt;Something is wrong!!!&amp;lt;/b&amp;gt;&amp;quot;, true, true);&lt;br /&gt;
dlgMessage(&amp;quot;Callback example&amp;quot;, &amp;quot;Testing&amp;quot;, true, false, my_message_cb, &amp;quot;some_param&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
function my_message_cb(param) {&lt;br /&gt;
   alert(&amp;quot;Message dismissed! The param was &amp;quot; + param);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgAlert ===&lt;br /&gt;
&lt;br /&gt;
dlgAlert is a convenience function for making it easier to show a warning message to the user. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgAlert(message, callback)&lt;br /&gt;
&lt;br /&gt;
// The above is equivalent to:&lt;br /&gt;
dlgMessage(&amp;quot;Message&amp;quot;, message, true, true, callback)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgWait ===&lt;br /&gt;
&lt;br /&gt;
dlgWait shows a message for a certain amount of time and then automatically closes itself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgWait(time_in_seconds, message)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Asking for input ==&lt;br /&gt;
&lt;br /&gt;
=== dlgQuery ===&lt;br /&gt;
&lt;br /&gt;
dlgQuery asks the user to respond to a question. The dialog shows a message, an input field for the user to type in, and Ok/Cancel buttons. The user&#039;s response is sent to a callback function that you must implement yourself. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(message, value, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| value || string || Default value to pre-populate the answer field with || Yes (but can be empty string)&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or their answer if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgQuery(&amp;quot;Enter a value:&amp;quot;, &amp;quot;my default value&amp;quot;, my_query_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_query_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      // User clicked the Cancel button&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&#039;Value is &#039; + value + &#039;, param is &#039; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dlgConfirm ===&lt;br /&gt;
&lt;br /&gt;
dlgConfirm is like dlgQuery, but just shows the Ok and Cancel buttons without an extra input field. The signature is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(message, callback, param)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Type !! Meaning !! Required&lt;br /&gt;
|-&lt;br /&gt;
| message || string (can be arbitrary HTML) || The question to show the user || Yes&lt;br /&gt;
|-&lt;br /&gt;
| callback || function || Function to be called when the user finishes with the dialog. The first parameter will be false if the user clicked the &amp;quot;Cancel&amp;quot; button, or true if they clicked the &amp;quot;Ok&amp;quot; button. || Yes&lt;br /&gt;
|-&lt;br /&gt;
| param || anything || Parameter that will be passed to the `callback` function as the 2nd parameter || No. Defaults to undefined.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Example usage is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dlgConfirm(&amp;quot;Are you sure you wish to do that?&amp;quot;, my_confirm_cb, &amp;quot;some_other_param&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
function my_confirm_cb(value, param) {&lt;br /&gt;
   if (value === false) {&lt;br /&gt;
      alert(&amp;quot;User clicked Cancel! Param was &amp;quot; + param);&lt;br /&gt;
   } else {&lt;br /&gt;
      alert(&amp;quot;User clicked Ok! Param was &amp;quot; + param);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// where &#039;param&#039; can also be ommitted.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Complete Example =&lt;br /&gt;
&lt;br /&gt;
The following code shows an example page (contained in {{Filepath|path=resources/a_example.html}} in the MIDAS distribution) of a custom page implementing most of the new features. You activate this page by putting in the ODB:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/Custom&lt;br /&gt;
  Path     /midas/resources&lt;br /&gt;
  Test     a_example.html&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The file &#039;&#039;&#039;a_example.html&#039;&#039;&#039; contains the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html class=&amp;quot;mcss&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;midas.css&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;controls.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;midas.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;script src=&amp;quot;mhttpd.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Example&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;style&amp;gt;&lt;br /&gt;
      .mtable td { padding: 10px; }&lt;br /&gt;
   &amp;lt;/style&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;body class=&amp;quot;mcss&amp;quot; onload=&amp;quot;mhttpd_init(&#039;Example&#039;);&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- header and side navigation will be filled in mhttpd_init --&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;mheader&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;msidenav&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;mmain&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;table class=&amp;quot;mtable&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;th colspan=&amp;quot;2&amp;quot; class=&amp;quot;mtableheader&amp;quot;&amp;gt;Status&amp;lt;/th&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td style=&amp;quot;width: 200px;&amp;quot;&amp;gt;&lt;br /&gt;
            Run number:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run start:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Start time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Last run stop:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Stop time&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Check box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- checkbox changes /Logger/Write data, fire dialog box on change (even if changed by odbedit) --&amp;gt;&lt;br /&gt;
            &amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;modbcheckbox&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modb&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot; onchange=&amp;quot;dlgAlert(&#039;Flag has changed&#039;);&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Color box:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;!-- box changes color according to /Logger/Write data --&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbbox&amp;quot; style=&amp;quot;width: 30px; height: 30px;&amp;quot; data-odb-path=&amp;quot;/Logger/Write data&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;lightgreen&amp;quot; data-background-color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Horizontal bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width:300px;height:20px;color:orange;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-print-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:500px;height:22px;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 500px; height: 18px;color:lightblue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbhbar&amp;quot; style=&amp;quot;width: 200px; height: 10px;color:lightgreen;background-color:white&amp;quot;&lt;br /&gt;
                 data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot; data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;mhaxis&amp;quot; style=&amp;quot;width:200px;height:22px;vertical-align:top;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                 data-max-value=&amp;quot;10&amp;quot; data-line=&amp;quot;0&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Vertical bars:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:right;&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;modbvbar&amp;quot;&lt;br /&gt;
                  style=&amp;quot;width:20px;height:200px;color:yellow;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot;&lt;br /&gt;
                  data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;20&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
            &amp;lt;span class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;width:10px;height:200px;vertical-align:top;color:red&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                  data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;quot;mvaxis&amp;quot; style=&amp;quot;width:100px;height:200px;text-align:left;&amp;quot; data-min-value=&amp;quot;0.1&amp;quot;&lt;br /&gt;
                                                                data-max-value=&amp;quot;10&amp;quot; data-log=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Thermometer:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:60px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-scale=&amp;quot;1&amp;quot;&lt;br /&gt;
                 onchange=&amp;quot;this.dataset.color=this.value &amp;gt; 9?&#039;red&#039;:&#039;blue&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;width:30px;height:100px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;blue&amp;quot; data-background-color=&amp;quot;white&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            Gauges:&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
         &amp;lt;td&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:50px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;darkgreen&amp;quot; data-background-color=&amp;quot;lightgrey&amp;quot; &amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div class=&amp;quot;modbgauge&amp;quot; style=&amp;quot;width:100px;height:65px;&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;0&amp;quot; data-max-value=&amp;quot;10&amp;quot;&lt;br /&gt;
                 data-color=&amp;quot;red&amp;quot; data-value=&amp;quot;1&amp;quot; data-scale=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- div around image with &amp;quot;relative&amp;quot; position as anchor for labels and bars --&amp;gt;&lt;br /&gt;
            &amp;lt;div style=&amp;quot;position:relative;width:300px;margin:auto&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;img src=&amp;quot;tank.gif&amp;quot;&amp;gt; &amp;lt;!-- background image of tank --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- label next to valve --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvalue&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-editable=&amp;quot;1&amp;quot;&lt;br /&gt;
                    style=&amp;quot;position:absolute;top:157px;left:288px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- vertical bar inside tank, render red if value &amp;gt; 9 --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbvbar&amp;quot; style=&amp;quot;position:absolute;top:80px;left:10px;width:104px;height:170px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-max-value=&amp;quot;11&amp;quot; data-color=&amp;quot;green&amp;quot;&lt;br /&gt;
                    onchange=&amp;quot;this.firstChild.style.backgroundColor=(this.value &amp;gt; 9)?&#039;red&#039;:&#039;green&#039;;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
               &amp;lt;!-- thermometer inside tank --&amp;gt;&lt;br /&gt;
               &amp;lt;div class=&amp;quot;modbthermo&amp;quot; style=&amp;quot;position:absolute;top:140px;left:20px;width:20px;height:100px;&amp;quot;&lt;br /&gt;
                    data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-min-value=&amp;quot;-10&amp;quot; data-max-value=&amp;quot;30&amp;quot;&lt;br /&gt;
                    data-color=&amp;quot;blue&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;tr&amp;gt;&lt;br /&gt;
         &amp;lt;td colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align: center;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- three buttons to change an ODB entry (run number in this example) --&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;1&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 1&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;5&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 5&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
            &amp;lt;button class=&amp;quot;modbbutton&amp;quot; class=&amp;quot;mbutton&amp;quot; data-odb-path=&amp;quot;/Runinfo/Run number&amp;quot; data-odb-value=&amp;quot;10&amp;quot;&amp;gt;Set run&lt;br /&gt;
               number to 10&lt;br /&gt;
            &amp;lt;/button&amp;gt;&lt;br /&gt;
         &amp;lt;/td&amp;gt;&lt;br /&gt;
      &amp;lt;/tr&amp;gt;&lt;br /&gt;
   &amp;lt;/table&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which results in the page shown in Figure 1 below:&lt;br /&gt;
&lt;br /&gt;
[[File:Custom17.png|frame|left|Figure 1  Example custom page using most features]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;clear: both&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &amp;lt;!-- clear wraparound after thumbnail --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Old custom page feature =&lt;br /&gt;
&lt;br /&gt;
There are a number of deprecated custom page features, which can be seen here: [[Old Custom Page Features]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:mhttpd Pages]] [[Category:Custom]]&lt;/div&gt;</summary>
		<author><name>Stefan Ritt</name></author>
	</entry>
</feed>