Chapter 2
Reactive Worksheets


2.1 Introduction

In this chapter, we look at the essential elements of simple reactive worksheets, i.e. worksheets in which behavior is defined directly in terms of visible features and user gestures.

There are four main topics that we need to cover - (1) the representation of page data as factoids, (2) the representation of user gestures as actions, (3) the use of operation definitions to specify how page data changes in response to user gestures, and (4) the use of view definitions to specify how visible features change in response to user gestures. After discussing these topics, we put all of the pieces together and show how to convert an arbitrary HTML page into a functional worksheet.

2.2 Page Data

The data underlying a web page appearing in a browser typically takes the form of a hierarchical data structure called a DOM (short for Document Object Model). The top node in this data structure represents the document, and its child nodes represent its components. Nodes in the DOM typically have attributes of various sorts (e.g. the width and height of a table); and, in some cases, those attributes are objects with attributes of their own (e.g. the style attribute of a node has attributes of its own (e.g. font family, font size, and so forth).

In order to use Dynamic Logic Programming to specify the appearance and behavior of a webpage, we need vocabulary to represent the state of a DOM in the form of factoids that express this state.

First of all, we assign identifiers for the nodes of the DOM that we care about. In order to give them meaning, we assign each of our identifiers as the value of the id attribute of the corresponding node. For example, if we wanted to use the identifier myinput to refer to the input element in the HTML fragment shown below, we would list that identifier as the id attribute of that input widget, as shown in this example.

<center> <input id='myinput' type='text' value='hello' size='30' style='color:black'/> </center>

Next, we invent predicates to describe the various properties of these nodes. See below for the most commonly used predicates. For example, we use the binary predicate value to associate an input node (type-in field or selector) with its value.

value(widget,value): This factoid is true whenever the value associated with widget is value. The widget here may be a text field, selector, radio button field, slider, and so forth.

holds(widget,value): This factoid is true whenever one of the values associated with widget is value. The widget in this case multi-valued selector or a checkbox field.

attribute(widget,property,value): This factoid is true whenever the property attribute of widget is value.

style(widget,property,value): This factoid is true whenever the property style of widget is value.

innerhtml(widget,content): This factoid is true whenever the innerHTML widget is content. Note that content must be a string of characters.
Given this vocabulary, we can encode relevant information in the form of a dataset. For example, the relevant state of the DOM fragment shown above can be represented by the dataset shown below.

value(myinput,hello)
attribute(myinput,size,30)
style(myinput,color,black)

2.3 Gestures

User interaction with a webpage takes the form of gestures (e.g. keystrokes and mouse-clicks). In order to talk about these gestures, we need appropriate vocabulary. For example, we use the unary operation click to represent the action of clicking on a button. We use the binary operation select to represent the action of selecting a specific option from a selector.

select(widget,value): This action occurs when the user enters or selects value as the value of widget. The widget here may be an object or text field, a selector, a multi-valued menu, a checkbox field, a radio button field, a slider, and so forth.

deselect(widget,value): This action occurs when the user erases or deselects value as the value of widget.

click(widget): This action occurs when the user clicks on widget.

tick: This action occurs periodically. By default, this happens once per second. It is also happens when the user clicks the Run button or the Step button in a simulation control box.

load: This action occurs when a page is first loaded.

unload: This action occurs when a user leaves a page.
We use this vocabulary to represent user gestures. For example, if the user clicks a button with identifier a, we represent this as the action click(a). If the user selects 3 from a selector with identifier b, we represent this as select(b,3).

2.4 Operation Definitions

Given a vocabulary for encoding data and gestures, we can describe the behavior of a worksheet by writing suitable operator definitions. The following examples illustrate how this can be done.

Consider the following buttons with identifiers orange, blue, purple, and black.

Suppose that we wanted the worksheet to change the color of this document (identified as page) whenever the user clicks one of these buttons. (Try clicking one of the buttons to see the desired effect.) This behavior can be described with the following operation definition.

click(orange) :: style(page,color,orange)
click(blue) :: style(page,color,blue)
click(purple) :: style(page,color,purple)
click(black) :: style(page,color,black)

Alternatively, we can write these rules more compactly by using a variable, as shown below.

click(X) :: style(page,color,X)

This rule reads as follows. If a user clicks on button with X as id, then, in the next state of the worksheet, the color style of the node identified as page should be X.

Although these operation definitions work fine, they are not quite complete. This is because, after clicking the above buttons, the state of our worksheet may include more than one fact of the form style(page,color,X). To completely specify the desired behavior, we need to remove the existing style factoids for page when a button is clicked. This can be done with the following operation definition.

click(X) :: style(page,color,Y) & distinct(X,Y) ==> ~style(page,color,Y)

This rule reads as follows. If a user clicks on button X, and the color of page is Y, and Y is different from X, then in the next state of the worksheet, the color of page should not be Y.

Now consider another example. Here we replace the four buttons with a selector with identifier pagecolor and four options orange, blue, purple, and black.

Let's suppose that we would like to change the text color of this document based on the value selected. We can describe this behavior with the following rules.

select(pagecolor,X) :: style(page,color,X)
select(pagecolor,X) :: style(page,color,Y) ==> ~style(page,color,Y)

The first rule states that, if a user selects value X for pagecolor, then style(page,color,X) should be true in the next state, i.e. the text color of the page should be X. The second rule say that, if a user selects value X for pagecolor and the style of page is Y and Y is different from X, then style(page,color,X) should not be true in the next state.

Unfortunately, this is not quite enough. Our action changes the color of the page, but there is nothing changing the value of the pagecolor attribute. As a result, it will reset to black after the gesture is processed. The following operation definition rules update the selector.

select(pagecolor,X) :: value(pagecolor,X)
select(pagecolor,X) :: value(pagecolor,Y) ==> ~value(pagecolor,Y)

As a final illustration, let's look at an example of interactions between input widgets. The operation definition rules for the four buttons in the first example change the color of the page correctly. However, they do not update the color indicated in the selector.

The operation definition shown below prescribes the desired behavior. When the user clicks a button with identifier X, then we want the value of the value of the selector to be updated and we want any previous value to be removed.

click(X) :: value(pagecolor,X)
click(X) :: value(pagecolor,Y) ==> ~value(pagecolor,Y)

Combining these rules with the rules shown above allows the user to click either the buttons or make selections and get the same effects in both cases.

2.5 View Definitions

In the preceding section, we saw that a single gesture can have multiple effects. For example, changing the value of a selector named pagecolor sets the value of the selector and changes the color of the page. To implement this behavior, we need to manage both conditions in our operation definitions, and we need to store both conditions in our dataset. Moreover, if we are not careful, our definitions might get out of sync with each other, and we would not get the behavior we want.

The good news is that it is sometimes possible to use view definitions to describe such behavior more economically and in a way that is less prone to mistakes. By defining some of our predicates as views of other predicates, we do not need to store as much information and we can get by with fewer operation definition rules.

In the case from the preceding section, suppose that we were to define the color of the page node in terms of the value of the pagecolor node. The definition is shown below.

style(page,color,X) :- value(pagecolor,X)

With this definition in place, we could replace the operation definition rules shown in the last section with the four shown below.

click(X) :: value(pagecolor,X)
click(X) :: value(pagecolor,Y) ==> ~value(pagecolor,Y)
 
select(pagecolor,X) :: value(pagecolor,X)
select(pagecolor,X) :: style(page,color,Y) ==> ~value(pagecolor,Y)

There are fewer rules here, and they mention fewer predicates. In particular, there is no mention of the style of the page. That property is fully determined by the value of pagecolor, and so we do not need to store or update this information in our rules. Instead, the worksheet computes the style using the view definition given above.

2.6 Putting It All Together

The easiest way to create a web-based interactive worksheet is to start with an HTML page and to add mark-up code to enable the functionality described here. The following is an example of an interactive worksheet built in this way. The page has some introductory text and a selector that lists four different colors. Making a different selection changes the the introductory text to the corresponding color.

<html> <head> <title>Simple Reactive Worksheet</title> <script type='text/javascript' src='http://worksheets.stanford.edu/epilog/javascript/epilog.js'> </script> <script type='text/javascript' src='http://worksheets.stanford.edu/worksheets/javascript/worksheets.js'> </script> </head> <body onload='initialize()'> <span id='mytext'>The following selector changes my color.</span><br/> <select id='textcolor' onchange='modselector(this)'> <option>orange</option> <option>blue</option> <option>purple</option> <option>black</option> </select> </body> <dataset id='lambda'> value(textcolor,black) </dataset> <ruleset id='library'> select(textcolor,X) :: value(textcolor,X) select(textcolor,X) :: value(textcolor,Y) ==> ~value(textcolor,Y) style(mytext,color,X) :- value(textcolor,X) </ruleset> </html>

This example illustrates the five things that must be done to convert an ordinary HTML page into an interactive worksheet.

First of all, the page must include directives to load the appropriate javascript libraries - the Epilog inference engine and the Worksheets code. This is accomplished in this example by the following lines in the head of the document.

<script type='text/javascript' src='http://epilog.stanford.edu/epilog/javascript/epilog.js'> </script> <script type='text/javascript' src='http://logicprogramming.stanford.edu/worksheets/javascript/worksheets.js'> </script>

Second, the worksheet must be initialized. This is accomplished by adding the Javascript code initialize() to the onload attribute of the body of the document, as shown below.

<body onload='initialize()'>...</body>

Third, as mentioned in an earlier section, every node that we want to control and every input widget that we want to handle must be given an id. In this case, the introductory text is named mytext.

<span id='mytext'>...</span>

Fourth, every input widget that we want to handle must be given an appropriate handler attribute. As described earlier, the specific handler must correspond to the type of the node. For example, a button would have modbutton(this) as its onclick attribute, and a selector would have modselector(this) as its onchange attribute.

<select id='textcolor' onchange='modselector(this)'>...</select>

Finally, we need to supply any initial data and our view defintions and operation definitions. The initial data is specified in a dataset element, and the rules are specified in a ruleset element. The dataset element typically has id set to lambda, and the ruleset element typically has id set to library. Furthermore, it is customary to specify a display style of none on these elements to prevent them from appearing to the user.

<dataset id='lambda' style='display:none'> value(textcolor,black) </dataset> <ruleset id='library' style='display:none'> select(textcolor,X) :: value(textcolor,X) select(textcolor,X) :: value(textcolor,Y) ==> ~value(textcolor,Y) style(mytext,color,X) :- value(textcolor,X) </ruleset>

Try saving the text at the beginning of this section as a file, load the file in a browser, and play around with it. Change the file in various ways and see what happens. For example, add additional options to the selector, additional text nodes, additional widgets, and so forth. Try changing the initial data and/or the rules.