// State machine to drive HMI_BOOL on a potentially laggy connection
const "_button_fsm" fsm {
on_dispatch "false" jump "released";
on_dispatch "true" jump "pressed";
on_dispatch "true" jump "pressed";
on_mouse "up" jump "shortpress";
on_mouse "up" jump "releasing";
on_dispatch "false" jump "released";
on_dispatch "true" jump "releasing";
on_mouse "down" jump "pressing";
on_dispatch "false" jump "released";
on_mouse "down" jump "shortrelease";
on_mouse "down" jump "pressing";
on_dispatch "true" jump "pressed";
on_dispatch "false" jump "pressing";
on_mouse "up" jump "releasing";
template "fsm", mode="dispatch_transition" {
apply "state", mode="dispatch_transition";
template "state", mode="dispatch_transition" {
| if(value == «@value») {
apply "jump", mode="transition";
template "fsm", mode="mouse_transition" {
apply "state", mode="mouse_transition" with "position", "$position";
template "state", mode="mouse_transition" {
apply "on-mouse[@position = $position]";
// up or down state is already assumed because apply statement filters it
apply "jump", mode="transition";
template "jump", mode="transition" {
| this.state = "«@state»";
| this.«@state»_action();
template "fsm", mode="actions" {
apply "state", mode="actions";
template "state", mode="actions" {
//| console.log("Entering state «@name»");
apply "*", mode="actions";
template "show", mode="actions" {
| this.display = "«@eltname»";
| this.request_animate();
template "hmi-value", mode="actions" {
| this.apply_hmi_value(0, «@value»);
template "widget[@type='Button']", mode="widget_class"{
const "fsm","exsl:node-set($_button_fsm)";
| class ButtonWidget extends Widget{
// | console.log("dispatch"+value);
apply "$fsm", mode="dispatch_transition";
| svg_root.removeEventListener("pointerup", this.bound_onmouseup, true);
// | console.log("onmouseup");
apply "$fsm", mode="mouse_transition" with "position", "'up'";
| svg_root.addEventListener("pointerup", this.bound_onmouseup, true);
// | console.log("onmousedown");
apply "$fsm", mode="mouse_transition" with "position", "'down'";
apply "$fsm", mode="actions";
| if (this.active_elt && this.inactive_elt) {
foreach "str:split('active inactive')" {
| if(this.display == "«.»")
| this.«.»_elt.style.display = "";
| this.«.»_elt.style.display = "none";
| this.bound_onmouseup = this.onmouseup.bind(this);
| this.element.addEventListener("pointerdown", this.onmousedown.bind(this));
template "widget[@type='Button']", mode="widget_defs" {
optional_labels("active inactive");