Skip to content
This repository was archived by the owner on Sep 25, 2018. It is now read-only.

SCION for Interaction Design and Web Front end Development

Jacob Beard edited this page Apr 5, 2016 · 3 revisions

Let's start with the simple example of drag-and-drop behaviour in the browser. You can run this demo live here.

An entity that can be dragged has two states: idle and dragging. If the entity is in an idle state, and it receives a mousedown event, then it starts dragging. While dragging, if it receives a mousemove event, then it changes its position. Also while dragging, when it receives a mouseup event, it returns to the idle state.

This natural-language description of behaviour can be described using the following simple state machine:

Drag and Drop

This state machine could be written in SCXML as follows:

<scxml 
  xmlns="http://www.w3.org/2005/07/scxml"
  version="1.0"
  profile="ecmascript"
  initial="idle">

  <state id="idle">
    <transition event="mousedown" target="dragging"/>
  </state>

  <state id="dragging">
    <transition event="mouseup" target="idle"/>
    <transition event="mousemove" target="dragging"/>
  </state>

</scxml>

One can add action code in order to script an HTML DOM element, so as to change its position on mousemove events:

<scxml 
    xmlns="http://www.w3.org/2005/07/scxml"
    version="1.0"
    profile="ecmascript">

    <datamodel>
        <data id="firstEvent"/>
        <data id="eventStamp"/>
        <data id="rectNode"/>
        <data id="rectX"/>
        <data id="rectY"/>
    </datamodel>

    <state id="initial-default">
        <transition event="init" target="idle">
            <assign location="rectNode" expr="_event.data"/>
            <assign location="rectX" expr="0"/>
            <assign location="rectY" expr="0"/>
        </transition>
    </state>

    <state id="idle">
        <onentry>
            <script>
                rectNode.textContent = 'idle';
            </script>
        </onentry>

        <transition event="mousedown" target="dragging">
            <assign location="firstEvent" expr="_event.data"/>
            <assign location="eventStamp" expr="_event.data"/>
        </transition>
    </state>

    <state id="dragging">
        <onentry>
            <script>
                rectNode.textContent = 'dragging';
            </script>
        </onentry>

        <transition event="mouseup" target="idle"/>

        <transition event="mousemove" target="dragging">
            <script>
                var dx = eventStamp.clientX - _event.data.clientX;
                var dy = eventStamp.clientY - _event.data.clientY;

                //note that rectNode, rectX and rectY are all exposed
                //from the datamodel as local variables
                rectNode.style.left = rectX -= dx;
                rectNode.style.top = rectY -= dy;
            </script>
            <assign location="eventStamp" expr="_event.data"/>
        </transition>
    </state>

</scxml>

There are then 4 steps that must be performed to go from an SCXML document to a working state machine instance that is consuming DOM events and scripting web content:

  1. Get the SCXML document, and convert it to a SCXML "model" object for easier interpretation.
  2. Use the SCXML model object to instantiate the SCXML interpreter.
  3. Connect relevant event listeners to the SCXML interpreter.
  4. Call the start method on the SCXML interpreter to start execution of the statechart.
<html>
    <head>
        <style type="text/css">
            html, body {
                height:100%;
                margin: 0;
                padding: 0;
            }

            div#rect{ 
                width:100px;
                height:100px;
                background-color:red;
                border:2px solid black;
                position:absolute;
                left:0px;
                top:0px;
            }
        </style>
        <script type="text/javascript" src="https://cdn.rawgit.com/jbeard4/SCION/2.0.14/dist/scxml.js"></script>
    </head>
    <body>
        <div id="rect"/>
        <script>
          var rect = document.getElementById("rect");

          scxml.urlToModel("drag-and-drop.xml",function(err,model){

              if(err) throw err;

              //instantiate the interpreter
              var interpreter = new scxml.scion.Statechart(model);

              //start the interpreter
              interpreter.start();

              //send the init event
              interpreter.gen({name:"init",data:rect});

              function handleEvent(e){
                  e.preventDefault();
                  interpreter.gen({name : e.type,data: e});
              }

              //connect all relevant event listeners
              rect.addEventListener('mousedown',handleEvent);
              document.documentElement.addEventListener("mouseup",handleEvent);
              document.documentElement.addEventListener("mousemove",handleEvent);
          });
        </script>
    </body>
</html>
Clone this wiki locally