Flapjax Tutorial

Welcome!

Welcome to Flapjax! This tutorial combines the specific with the abstract. We'll begin by discussing pure client-side programming, then describe obtaining data from third-party servers, and finally discuss storing data on the Flapjax server.

This tutorial contains links to the demos. We encourage you to not only run them but also read their sources. We've tried to create clean and readable code for you to learn from. The tutorial will be even most effective if you run the examples in this tutorial for yourself.

Once again, welcome to Flapjax. Please fasten your seat belts!

Introducing Behaviors

The easiest way to start programming in Flapjax is to use the templating syntax, which lets you embed Flapjax code in an HTML page. Here's a simple example:

<p> The time is {! timerB(100) !}. </p>

In this fragment, the {! (pronounced curly-bang) and !} surround an expression written in Flapjax. The expression's syntax looks remarkably similar to that of JavaScript, and this is no coincidence: Flapjax shares the syntax of JavaScript. You may not be sure of what it does, but you can probably guess: the function timerB probably returns the current system time. That is, the page probably prints the system time at the point when it was loaded. Let's check by running the program .

Hopefully what you saw is not just the current system time, but the time incrementing rapidly. The function timerB constructs something called a behavior, which is a value that changes over time. In the first part of this tutorial you'll learn quite a bit more about behaviors, which are central to Flapjax.

Returning to the example, the expression timerB(100) creates a timer behavior that returns a value every 100 milliseconds, i.e., ten times every second. What it returns, however, is a microsecond timer value, and all those extra digits are what make the timer appear to be updating madly. So to smooth it we should divide the value by 1000 :

<p> The time in seconds is {! Math.floor(timerB(100) / 1000) !}. </p>

First, see how it runs. As you can see, this value updates more sedately, once every second.

Here's what is interesting about this example: the division and floor happened every time there was a new value for the timer! We didn't need to tell the program to recompute: when the value of timerB changed, the rest of the computation automatically noticed the change and updated itself. In other words, not only did the value of timerB(100) change over time, so did the value of timerB(100) / 1000 and, in turn, the value of Math.floor(timerB(100) / 1000) . This points to an important rule: if an expression is a behavior, all expressions whose values depend on it also become behaviors. This automatic updating of the values of behaviors is central to Flapjax.

We don't actually have to write all expressions inside the HTML document: we're welcome to, and should, move more complex expressions into a script tag, where we can give them names that we can refer to later. Let's do this:

<script type="text/flapjax"> var timerB = timerB(100); var secondsB = Math.floor(timerB / 1000); var secsModTenB = Math.floor(secondsB % 10); </script>

The script header tells the Flapjax compiler that the script code is in the Flapjax language. The compiler converts this into pure JavaScript code, so that it can run on standard browsers without any plugins. Notice how the definitions resemble regular JavaScript code, with the sole exception of the use of behaviors.

Having created such definitions, we can now use them in our pages. For instance, we could ask whether the current system time, in seconds, is even or odd:

<p> The current system time is {! ((secondsB % 2 === 0) ? "even" : "odd") !}. </p>

Or, suppose in a separate JavaScript script tag we had defined the function arrayToString . We could apply this function to a behavior inside Flapjax:

<p> The list of time modulo 10 elements is {! arrayToString(oneToN(secsModTenB)) !}. </p>

Again, see how it runs. Notice how arrayToString , even though written in JavaScript, automatically works over behaviors, and does the right thing: it recomputes a fresh array as the time changes every second. This process, of making a function written in JavaScript operate over behaviors, is called lifting: it ``lifts'' the function from operating over JavaScript values to operating over Flapjax behaviors. The Flapjax compiler performs lifting for you automatically, so you should rarely, if ever, have to encounter it yourself. If, however, you treat Flapjax simply as a JavaScript library (which is a mode of operation we support), you'll find yourself performing lifting explicitly.

The examples used in this introduction to behaviors warrant two disclaimers. First, you probably shouldn't rush to uselessly display such timers on your Web pages: they remind people of the worst abuses of JavaScript from the 1990s, and will make your site unpopular. Second, this tutorial has shown only excerpts of the full pages you see when you run the programs. There is some missing code that you must write and some that you should write. The must-write code creates a proper Web page and loads the Flapjax library. The should-write code provides meaningful messages when the Web browser has disabled (or does not support) JavaScript. You should read the annotated versions of the demos to understand these details.

At this point, you can either soldier on with the tutorial or you can stop to read the demos and create your own working Flapjax pages. We suggest you consider doing the latter. The joy of a programming language is that you can actually write programs in it. As the tutorial's examples get more complex, you'll find it helpful (if, occasionally, frustrating) to be able to not only see what we present but to also tweak the examples and see what impact your changes have.

Altering Content Based on Behaviors

Think about the position of the mouse. Whenever you look it has a value, but the value keeps changing. If we write a program that makes some entity depend on the current position of the mouse, we'd want that entity to be updated whenever the mouse moves. Fortunately, this is easy because the mouse position is another built-in Flapjax behavior.

Here's an example that draws a text box at the current mouse position. The code looks a little dense, but it's actually quite simple once you parse it:

<div id="themouse" style={! {color: '#FFFFFF', backgroundColor: '#000000', position: 'absolute', left: mouseLeftB(document), top: mouseTopB(document), padding: '10px'} !}> the mouse </div>

This creates a div element and sets its style. The most important attributes are left and top , which are set to be those of the mouse. But because Flapjax provides these as behaviors, the values of the style setting update automatically, moving the box to follow the mouse. See the mouse move.

The example above employs a syntactic convenience implemented by the Flapjax compiler. Inside the delimiters, the expression has the form { ... } . This syntax creates a literal object in JavaScript. A object is just an aggregate that maps names to values; an HTML style is also just an aggregate that maps names to values. When a Flapjax expression evaluates to an object, the delimiters therefore automatically splice the object into the context using the CSS attribute-value notation. This shorthand makes it exceptionally easy to define an object containing behaviors and use these to manipulate style attributes of page entities. (Note that the Flapjax expression can even evaluate to an object; the example above is a special case, where the expression is syntactically an object.)

Now let's add some complexity to this example. Suppose our mouse has a somewhat lazy tail, which takes a little while to catch up with the rest of the mouse. That is, the tail's current position is where the mouse was a little while ago; in other words, the tail's position is a delayed version of the mouse's position. This is easy to express in Flapjax:

<div id="tail" style={! {color: '#FF0000', backgroundColor: '#000000', position: 'absolute', left: delayB(mouseLeftB(document), DELAY) + $('themouse').offsetWidth, top: delayB(mouseTopB(document), DELAY), padding: '10px'} !}> its tail! </div>

The key expression is

delayB(mouseLeftB(document), DELAY) + $('themouse').offsetWidth

The sub-expression on the left delays the mouse's left position by some value given in the constant DELAY (defined elsewhere), creating a new behavior. The one on the right references the object on the current page whose whose CSS id is themouse , and computes its width. The sum of these yields the new horizontal position of the mouse. Watch the mouse's tail follow it.

Now you should try to write more complex examples yourself. For instance, suppose we want another segment of the tail that not only follows the rest of the mouse but also wags. (Do the tails of mice wag? We don't really know: a quick survey around the office was inconclusive.) You may not succeed, but you'll learn from the effort. When you're done, see all the code.

Once again, our lawyers have asked us to warn you that pages that gratuitously contain such behaviors are annoying, so please use them only when necessary or when you're trying to annoy your users.

Reflecting Content as a Behavior

We've seen how the content of a page can depend on a behavior, thereby updating when the behavior's value changes. We can go the other way, too: create a behavior that depends on the content of a page, so when the page's content changes, so does the behavior. (And if something else on the page depends on that behavior, it'll update too...and so forth.)

Suppose you have a form that contains a field whose content must be precisely three characters long. It would be nice to use the border color of the field to indicate whether or not its content satisfies this constraint. Notice that whether or not the constraint has been satisfiedthat is, the boolean value representing satisfactiondepends on the current content of the form field; the color of the border in turn depends on this boolean value. Expressing all of these as behaviors ensures that the language automatically maintains the dependencies between them.

First, here's the fragment of an HTML page that creates the field:

Enter a 3 character string: <input id="textField1"/>

We want to modify the border color attribute of the input field depending on whether or not the length of the field is correct. The function extractValueB converts the value of an element on the page into a behavior. Given this helpful function, the expression that determines the background color becomes

(extractValueB('textField1').length === 3 ? 'green' : 'red')

So all that's left is to use this behavior to set the border color:

Enter a 3 character string: <input id="textField1" style={! {borderColor: (extractValueB('textField1').length === 3 ? 'green' : 'red')} !}/>

This kind of inline expression is not always desirable, nor even always feasible:

It's sometimes undesirable because it clutters the page, intermingling presentation with behavior. It is often better to move the validation code into a script tag, so the HTML portion of the page remains unencumbered by scripting. Indeed, because Flapjax provides the ability to convert page elements into behaviors, it's easy to retroactively add scripting to named id s on a page without having to modify its HTML. Some people recommend this strategy of employing unobtrusive JavaScript . If you're starved for buzzwords you can also think of this strategy as a kind of aspect-oriented programming , where the Flapjax script is advising the page, which is oblivious to Flapjax. In this tutorial we'll try to speak in plain English, though.

tag, so the HTML portion of the page remains unencumbered by scripting. Indeed, because Flapjax provides the ability to convert page elements into behaviors, it's easy to retroactively add scripting to named s on a page without having to modify its HTML. Some people recommend this strategy of employing . If you're starved for buzzwords you can also think of this strategy as a kind of , where the Flapjax script is advising the page, which is oblivious to Flapjax. In this tutorial we'll try to speak in plain English, though. It may not be feasible because the constraints are not all local. In the example above, the only constraint on the input field was its own length. But in a more complex form, the constraints may be hierarchical or even mutually-dependent. In such cases it becomes highly advisable to lift the scripting into a script tag. That also makes it possible to define functions that capture common validation options (such as checking for the length of a string using the appropriate comparison operator).

See the longer example , which demonstrates these concepts.

If you have prior experience programming in JavaScript, you will find it instructive to compare the use of Flapjax (whether in Flapjax syntax or in raw JavaScript syntax) to having to program the same behavior entirely using callbacks.

Intermezzo: The Spreadsheet Grew Up

Since we're catering to multiple audiences, we sometimes digress into more conceptual material. You can always spot a digression because it's in a separate section labeled as an intermezzo (such as this one). If all you want is the nitty-gritty of programming in the language you can safely ignore these digressions, but if you want to understand how it relates to other topics in programming, you may find these enlightening. Of course, you can always skip the intermezzos in your early readings and come back to them later.

Spreadsheets are a good idea. Not in the sense that it's fun to spend all day crunching numbers arranged in rows and columns; rather, in the sense that they let you express dependencies between cells, then automatically perform the ``heavy lifting'' of propagating these dependencies. They are, in some respect, the ultimate kind of declarative programming language, but without any associated religious cult.

Behaviors are essentially the natural extension of the spreadsheet model, generalized in two respects:

You can program over arbitrary data, not just numbers and a few other impoverished datatypes. You don't have to write everything in a grid and use an unscaleable naming convention. Instead, expression A depends on the value of expression B if B is a sub-expression of A (which includes B being an argument to a method and A being the method invocation, or even B being an expression in a method that A invokes). If you do need to name an expression, you can employ the naming power of JavaScript, which includes not only variable names but also array indices, objects fields, and so forth. Just pick the one that works best for your needs.

That said, behaviors are still in the spreadsheet mould of expressing dependencies and letting the language sort out the propagation of values. Like most spreadsheets, Flapjax has optimizations to avoid wasteful computation; unlike most spreadsheets, Flapjax has mechanisms to handle quite sophisticated dependencies including mutual references (which often arise in programming visual user interfaces).

One way of view Flapjax, then, is that it tries to reconcile two important programming styles that have needlessly been in conflict: declarative and imperative programming. Imperative programs often put too much book-keeping burden on the programmer; where the programmer fails, the program generates errors owing to inconsistencies. In contrast, traditional declarative programming forces programmers into a straightjacket that sometimes even ignores the presence of imperative effects in real-world data and systems. In contrast to both of these, Flapjax fully embraces mutationbehaviors are entirely built around the existence of mutationbut encourages programmers to describe their systems declaratively, pushing the burden of propagating these changes through the declarative specification onto the language. In short, Flapjax programs tend to be declarative specifications over imperative data.

Discrete Streams

The time, the position of the mouse, the content of a field: these all have a value continuously, i.e., at all points in time. It's just that the value may change during observation, so all expressions dependent on these values must have their values updated to keep the system in a consistent state. Flapjax automates the updating of these values. This is the essence of behaviors.

Now consider the following phenomena: the clicks of a mouse, the keystrokes at a keyboard, or the arrival of network packets. These are discrete. Over time there may be any number of values, perhaps even infinitely many, but a value may not arrive just when we look; in fact, there may never be a value ever again (if, for instance, the connection was unplugged). This is especially typical of the communication patterns of external devices.

Because they are discrete, and because there isn't always a ``current'' value, behaviors are a poor model for these phenomena. We don't want to poll for values because this may cause the system to pause indefinitely. Instead, we want the arrival of a new value on this discrete stream to push computation through the rest of the system, updating dependencies and keeping the system consistent. In Flapjax, we refer to such streams as event streams.

Programming with event streams is a little different from programming with behaviors. A behavior masquerades as an ordinary JavaScript object whose content just happens to change automatically. In contrast, an event stream is a new kind of value, with new primitives for programming over it.

Let's start with a very simple example: given an input box, we want a program that prints the length of the string in that box. We can write this in terms of behaviors, but it is conceptually clean to also think of each keystroke as an event, and of wanting to only update the value when an event occurs. The program first creates a text box:

Text buffer: <input type="text" id="toLength"></input>

The program then uses extractValueE to create an event stream reflecting all events associated with this text box. (In practice, this stream includes not only keystrokes but also tabbing and other activities. extractValueE takes additional arguments that help refine the set of events.) We want to then transform this event stream into a series of strings representing the length:

<p> Length: {! extractValueE('toLength').mapE( function (s) {return s.length; }) !} <p>

The method mapE iterates the supplied function over the given event stream, generating a transformed event stream as its output. In this case, it iterates the event stream of the string in the toLength entity into a stream of the lengths of the strings.

For a second example, suppose we have a text box and we want to save the box's content as a draft. This requires a server on which to save it. We're only describing client-side programming for nowwe'll get to servers in a bitso for the moment, let's focus on how to program the client-side functionality. The Web page contains a text box, a button to click on to save drafts, and space to indicate when the draft was last ``saved'':

Last Saved: <span id='savedTime'></span> <form> <textarea id='theText'></textarea> <br> <input type='button' id='saveButton' value='Save Now'></input> </form>

Every time the user clicks on the button, we want the time to update (and, in a future version of this program, the content to be saved on a server). First we'll create variables that represent the different DOM objects in the page (the `D' suffix helps us remember that these are references to DOM objects):

<script type="text/flapjax"> var textD = $('theText'); var saveD = $('saveButton'); var savedTimeD = $('savedTime'); ... </script>

Now we want to extract the event stream of button clicks; every time there's a click, we want to update the time of last save:

var saveClickE = extractEventE(saveD, 'click'); var savedTimeE = saveClickE.mapE(getTime); insertValueE(savedTimeE, savedTimeD, 'textContent');

getTime

<script type="text/javascript"> var getTime = function (_) { var date = new Date(); return date.toLocaleString();} </script>

whereis a JavaScript function:

Watch this run. You notice that there's something rather unsatisfactory about it: it only registers drafts when the user clicks the button, which means a user immersed in their work who forgets to click may lose their draft. Instead, we'd like to use a timer that periodically saves the text:

var autoSaveE = timerE(100); var savedTimeE = autoSaveE.mapE(getTime); insertValueE(savedTimeE, savedTimeD, 'textContent');

While this is much better, this isn't quite right, either: now the auto-save happens only when the timer fires. What we'd really like is an auto-save feature that saved both when the user clicks and when the timer fires. That is, given an event stream corresponding to the timer and another corresponding to the user's clicks, we'd like one that merges these two to form a unified trigger for saving drafts. This is precisely what the Flapjax operator mergeE does:

var saveClickE = extractEventE(saveD, 'click'); var autoSaveE = timerE(10000); var saveE = mergeE(saveClickE, autoSaveE); var savedTimeE = saveE.mapE(getTime); insertValueE(savedTimeE, savedTimeD, 'textContent');

Be sure to study the full code.

Intermezzo: Contrasting Behaviors and Event Streams

Event streams and behaviors offer complementary views of the world. It is easy, however, to overstate their differences. Given an initial value, every event stream can be converted into a behavior: the behavior always has the value of the last event to have arrived on the stream, starting with the specified initial value until the first event arrives. Likewise, every behavior can be converted into an event stream: when the behavior's value changes, send the new value as an event. These conversions are handy enough that Flapjax provides them as primitive functions called startsWith and changes , respectively. (These are not the sole means of converting between the two, only the most natural ones.)

Though some phenomena are more naturally thought of as one or the other, in some cases the same phenomenon can be thought of as either, depending on the context. As a rule-of-thumb, use behaviors to represent internal state and when the computation that expresses a dependency between two values won't run for too long. When the state is in some external medium, odds are the communications channel can be unpredictable, making event streams more suitable. Similarly, if a computation has the potential to take too long and you may need to time it out, it's better to use event stream and merge in timeout behavior, as we saw in the buffer draft saver example.

Dot Notation

Earlier we saw the following program:

(extractValueB('textField1').length === 3 ? 'green' : 'red')

.length

extractValueB

length

extractValueB('textField1').length

Static Initialization

This fragment actually contains a small sleight-of-hand. Notice the use of: we treated the result ofas if it were a regular object and dereferenced itsfield. Not only is this perfectly legal in Flapjax, it behaves as you'd expect: the value of the entire expressionis defined to be the length of that entity at all points in time, which means its value updates as the entity's content updates.

As you may have noticed from some of the demo programs, we sometimes use the notation ||| (which we pronounce super-or). This is associated with the {! and !} delimiters. Everything on the left of the super-or is the Flapjax code; but because it may take some event to invoke this program, the right of the super-or specifies what HTML to use initially. We call this the static initializer.

A static initializer is especially important when using event streams where the programmer cannot predict when the first event will occur, but it's a good idea to provide one even when using behaviors. Sometimes there may be a moment or two before the code on the page begins to execute. More likely, though, if a static initializer appears in place of a behavior's value after a few seconds, it often indicates a problem: either JavaScript has been disabled, or the library is not loading, or the program contains an error.

Obviously, static initialization is a linguisitic concept that exists even in the absence of templating syntax. Every event stream supports the method startsWith to enable the programmer to provide the static initialization value.

Intermezzo: Where are the Callbacks?

If you're experienced in JavaScript or in using GUI toolkits, and you've been paying attention to the examples, you must have noticed an important absence: callbacks! Whereas callbacks are the cornerstone of most traditional interactive and event-driven programming, the Flapjax style seems to be entirely devoid of them. Why?

Most programs can usefully be thought of as black-boxes that consume some input (possibly an infinite stream of them) and producing corresponding output; that is, the output depends on the input. The callback, however, is invoked by some application-independent part of the system; it therefore reverses the natural order of dependency. The problem is even easier to see in a language with static type declarations: because the callback is invoked by an application-independent framework, it returns nothing (or, at best, a generic status code). As a result, a callback cannot compose with any other application-specific method. The use of callbacks therefore forces the use of mutation and other imperative effects, even when these are not conceptually necessary.

There is still value to callbacks, primarily because they enable a data-driven style of computation. That is, rather than the output constantly polling the input for data, the system can stay dormant until the input has fresh data to generate new output. To keep this benefit, Flapjax evaluates its updates by pushing rather than pulling.

To summarize, while callbacks are a very useful programming mechanism and have a useful place in programming, their use often obscures important details of program structure. For this reason, Flapjax programming style tries to avoid the use of callbacks. We encourage you to give it a try. It does sometimes force you to rethink your program structure, but invariably you'll find this is making you think less about imperative operations and more about your underlying data models. By thinking harder about the relationships between your data you write more declarative specifications, leaving more of the work of maintaining consistency to the language. Be lazy: Larry Wall and Randal L Schwartz called it one of the ``three great virtues of a programmer''!

Accessing Data from Foreign Servers

The security model of JavaScript prevents remote procedure call (RPC) requests from being made to sites other than the one where your page originated. This makes it difficult to load data from other sites.

Several people have proposed a variety of techniques to work around this shortcoming. One common technique relies on the following observation: even though you cannot make an RPC request to a different host, you can load the content of a JavaScript script tag from it. The resulting JavaScript is then evaluated with the rest of the code on your page. It can, for instance, therefore be a program that simply defines the datum you were trying to download. For instance, the Web site del.icio.us offers a limited number of your bookmarks in this form.

Writing the code to perform this operation is laborious. You have to first create a script, insert the reference to the remote site, wait for the data to download, and extract the resulting data. The script binds the result to a fresh variable, so you would want to remove this variable from your namespace once you've obtained its value. If your page is interactive, you will need to do all this on a repeated basis. Therefore, Flapjax provides a primitive that implements this behavior, and the primitive works over event streams (natch).

Let's consider the del.icio.us example concretely. Connecting to a particular URL that includes the username of a del.icio.us user downloads JavaScript code which, when run, binds the variable Delicious to some of that user's bookmarks. The Flapjax interface to this mechanism is evalForeignScriptValE , which consumes an event stream of requests. Suppose userName were a field in which the user entered a del.icio.us username:

var userNameChangeE = extractValueE('userName'); var deliciousUserLinksE = evalForeignScriptValE( {'url': 'http://del.icio.us/feeds/json/' + userNameChangeE, 'globalArg': 'Delicious'});

Every fresh username places a new value on the event stream, which leads to a fresh iteration of the remote site call. Each time the Flapjax function extracts and returns the value bound to the named global argument, in this case Delicious . Run the program.

Observe that an event stream is the natural argument to this Flapjax function. Even though event streams and behaviors are interchangeable, we want to think of connecting to a foreign Web site as a discrete activity rather than a continuous one. Thus, even though it is natural to think of the value of the field as a behavior, we only want to connect to the foreign site when the value changes. Indeed, this is a case where it is simply not clear what it would mean for evalForeignScriptVal to consume a behavior instead of an event streamor, at the very least, how to be a good citizen of the Internet in the process.

Though we are not continuously connecting to the Internet, we are still connecting after every change. This leads to a slightly jerky interaction, and perhaps also fetches the bookmarks of usernames we weren't interested in. Rather, it is more natural to connect only when the user pauses. This is a sufficiently useful construction that Flapjax provides two useful functions for this purpose: filterRepeatsE and calmE . The former eliminates duplicates from an event stream. The latter takes an additional argument, a duration, and only releases the latest event when the duration has elapsed and no new event has occurred. Putting these together:

var deliciousUserLinksE = evalForeignScriptValE( {'url': 'http://del.icio.us/feeds/json/' + calmE(filterRepeatsE(userNameChangeE), 450), 'globalArg': 'Delicious'});

Run the revised program.

This is all quite a bit of fun, so we can't warn you strongly enough that it's also very dangerous: you're downloading code from a random Web site and running it right within your browser! (We don't even need lawyers to tell you this. Just thinking about it is enough to turn us into lawyers.) You really, really don't want to do this until you have good reason to trust the remote site. Even then, you should carefully examine the code they're generating. And even then, you should check it repeatedly because it may change in ways you find undesirable. And even if you do that, your classical education has taught you to keep in mind Bertrand Russell's chicken.

That's not all. Flapjax offers much more to enable you to program over Web services. Take a look at Yaggle, a client-side mashup that uses Yahoo!'s local services directory in conjunction with Google's mapping capability. The key to this mashup is the Flapjax primitive getWebServiceObjectE , which simplifies the process of both constructing the service call and, even more usefully, parsing the response by converting XML responses into JSON.

Server-Side Persistence

Let's go back to the draft saver. Obviously this is only useful if we can store drafts on an actual server, and access it at a later date. Flapjax provides a server where you can save your data! Because the server conceptually stores Flapjax values, it automatically propagates changes to these values to all clients accessing them. (In practice, of course, the Web makes it difficult to implement such an interface, so for now clients work by polling the server. As Web standards evolve, Flapjax will exploit them without you, the programmer, having to rewrite your code.)

To make a value persistent, we must specify do three things. First, we should give the value a place to reside on the server, akin to a filename. Second, we should specify how we want to initialize and subsequently read the value from the server. Third, we must specify how to write changes to the server. This separation proves to be quite flexible, and lets us implement a range of policies depending on our needs.

Concretely, recall that the draft was stored in an element labeled theText . We should initialize the draft with its contents from the last time we edited it, which we do as follows:

<textarea id='theText' value={! readPersistentObject({path: ['draftFT'], initial:''}) !}/>

We could, of course, have chosen to not read the value that was previously there. The initial attribute simply indicates what value to use the very first time, before the persistent object has been created.

In addition, we must write changes back to the server. This couldn't be simpler: we just state where to store the value, and what to store there. In this case, every time there is a event on saveE we want to store the current value of theText : that is, we want to snapshot the value of that field:

writePersistentObject(saveE.snapshot($B('theText')), {path: ['draftFT']});

Finally, we have to initialize the server and load the appropriate libraries. See the example for the minutiae. Notice that you can run the application and open the same URL in two windows or frames; you'll eventually see changes in one propagate to the other.

Intermezzo: Isn't This Model-View-Controller?

Software engineering textbooks love to tell you how you must use Model-View-Controller (MVC), which is a useful but heavy-weight way to think about structuring an application. Flapjax gives you MVC, but with a twist. You want to think of the server as holding the model, and the client as implementing the view. The controller is the annoying part: updating values, distributing updates, propagating updates, and so forth. Flapjax is the glue that implements the controller for free. It's MVC without the bondage and discipline.

Conclusion

This concludes our tutorial on Flapjax. Congratulations on making it this far! There is more to the language, as you'll learn from reading the documentation and building your own programs. Tell us about the cool programs you build!