SVG as an essential component of a Web application authoring framework

Julien Quint

Takanari Hayama

IGEL Co., Ltd.
Homest Musashino Bldg. 8F
1-13-3 Naka-cho, Musashino-shi
Tokyo, 180-0006 Japan
{julien,taki}@igel.co.jp
http://www.igel.co.jp/

Abstract

We present a Web application authoring framework codenamed Bender, consisting of a markup language for high-level application and component description, a Javascript runtime engine for Web browsers and SVG players, and a library of Javascript support code and predefined components. We describe the main concepts of the framework such as components, views, controllers, and how a Bender application is run. As a concrete example, we give a detailed description of a simple application, as well as brief descriptions of a more complex application built using SVG.

Introduction and Rationale

The need for more powerful tools to build application is a constant in software engineering, and the domain of Web applications is no exception. We can distinguish different trends, such as complex IDEs (see for instance Maqetta) or Javascript frameworks (e.g., general purpose toolkits such as jQuery or more specific libraries such as Backbone.js); not to mention the related activity around Web frameworks, servers and databases on the backend. In this paper however we will focus on the frontend exclusively.

An interesting aspect of Web applications is that they rely for a large part on declarative languages (HTML, CSS, SVG, and often microformats based on XML or JSON), and not just scripts. This is not specific to Web applications; IDEs often make use of XML files to store data about projects or interface layout. However, in the case of Web applications, this reliance on markup languages is made very explicit: developers and designers usually have to author markup data, and a lot of the GUI code is devoted to manipulate it through the DOM or abstractions over the DOM. Following this observation, our approach is thus to move a lot of the application logic to the declarative space, where we believe it is easier to understand, reason about and manipulate; by using schemas, transformations or DOM access.

The Web application authoring framework that we present here, codenamed Bender, is being developed from scratch with as few dependencies as possible. Reasons behind this choice include: 1) independence from Javascript frameworks that may come in and out of fashion, while leaving the option for developers to still use their favorite libraries; 2) the ability to run outside of mainstream environments: e.g., a set-top box, or the control panel of a home appliance, running only an SVG Tiny player, rather than a full-blown Web browser as can be found on major desktop and mobile operating systems; 3) easy integration with server-side Javascript solutions such as node.js; and 4) future integration with GUI designer tools for both application logic and layout.

This paper describes Bender, which is currently under active development; the current implementation is still in a very early state, but following the principle of "release early, release often", we offer it for public scrutiny already in the hope of generating interest and feedback to help further its development. Bender is free software and is available under the Apache License v2.0.

Concepts

Bender follows loosely the well-known Model-View-Controller pattern. Models contain data (often backed by a back-end database) that is presented to the user through views. These views can be interactive, allowing the user to manipulate the data, the controllers managing the flow of data between views and models. In Bender, this pattern is implemented through the following concepts:

Application description and runtime
In Bender, an application is not a standalone executable but a description of different components and how they interact with the user or with other components. As such, a Bender application is really a document in the DOM sense. We use XML for the concrete representation of applications and components, which can be run through an interpreter, implemented inside a Web browser or an SVG player. (We'll discuss other strategies for running an application below.)
Component
The component is a unit of functionality in Bender. A component can describe its own rendering, respond to and send events, and include other components.
View
The view of a component is the description of how it will get rendered. Bender is content-agnostic, so that any language that the runtime application implements can be used, the more common examples being HTML and SVG.
Event
Events are familiar to Web application developpers since they are an integral part of the DOM. In Bender, we extend the use of events to not only DOM elements (although these are used extensively), but also to allow inter-component communications through custom Bender events.
Controller
A controller is an element that is part of a component and backed by a Javascript delegate object. Controllers can listen to events (both DOM and Bender events), and maintain connections to view elements or other components through outlets. All components have a main controller, and optional additional controllers (e.g., an editor component would include a clipboard controller to manage cut, copy, paste and delete, as well as command controller to manage undo and redo.) Every controller is backed by a Javascript delegate object that maintains its list of outlets, has methods for event handling, etc.

In the following section, we will see an example of how these concepts are represented in Bender and how they fit together.

A Sample Bender Application

Let's look in detail at an actual, albeit very simple, Bender application. The full description of the application follows:

<app xmlns="http://bender.igel.co.jp" xmlns:html="http://www.w3.org/1999/xhtml">
  <title>Welcome to Bender!</title>
  <controller>
    <listen source="button" event="@pushed">
      alert("You're welcome!");
    </listen>
  </controller>
  <root-view>
    <html:p>
      Welcome to Bender!
    </html:p>
    <html:p>
      <component href="../lib/button.xml#button" id="button">Thanks</component>
    </html:p>
  </root-view>
</app>

Here we see the concrete XML syntax of a Bender application. (Note, however, that Bender being still under active development, some details of the syntax may change in the future.) This application may be seen in action at the Bender website. The screenshot below shows the state of the application after the "Thanks" button was clicked:

Welcome to Bender! application running

The top-level element for an application is app. We also introduce the Bender XML namespace (http://bender.igel.co.jp) that includes all Bender elements. In general, Bender documents contain elements from multiple namespaces since they usually include view descriptions from other namespaces (in this example, HTML, hence the XHTML namespace; this could also include SVG, of course.)

The title element is a metadata element that can be used to title components. The title of an application can be displayed in the title bar or the current tab of the host browser, as is the case here.

Let's look at the root-view element first. This element describes how the application is rendered in the target application. The default Bender interpreter provides either an HTML div or an SVG g element in which the contents of the root-view element are copied. Here we see two paragraphs; the first one is a plain HTML element, while the second paragraph contains a Bender element, component.

This element causes a new instance of a button component (as specified by the href attribute) to be created, with the contents of the element (the text node "Thanks") as the contents of the component. We'll have a look at this component below; for the moment, we'll just note that it renders a button that listens to mouse clicks or touch events and generate a @pushed event in response. (We use at @ sign to prefix Bender event names by convention.)

Now that we have a button, we want to listen to the events that it generates. This is the job of the application controller described in the controller element. This controller is very simple and contains a single listen element that associates an event handler with an event from a given source. Here, the source attribute is used to identify the node or component that is listened to, which is the button component with the same id. The event attribute is used to specify which event we are interested in. The content of the element is Javascript code for the handler.

The result is an application that displays a message and a button; when the button is clicked (or tapped), an alert box is displayed as pictured above. (The green "OK" box is a status from the interpreter showing that the application is running correctly.)

We see from the application description that it includes an external button component. This component is part of the Bender component library and is defined as follows:

<component xmlns="http://bender.igel.co.jp"
  xmlns:html="http://www.w3.org/1999/xhtml" id="button">
  <title>Push button</title>
  <stylesheet>
    .bender-button { display: inline-block; cursor: default; padding: 4px;
      border: solid thin black; background-color: #ddd; }
    .bender-button.__down { opacity: 0.5; }
    .bender-button.__disabled { opacity: 0.5; }
  </stylesheet>
  <controller>
    <script>

      $_.handleEvent = function(e)
      {
        e.preventDefault();
        if (flexo.has_class(this.outlets.button, "__disabled")) return;
        if (e.type === "mousedown" || e.type === "touchstart") {
          flexo.add_class(this.outlets.button, "__down");
        } else if (e.type === "mouseup" || e.type === "touchend") {
          if (flexo.remove_class(this.outlets.button, "__down")) {
            setTimeout((function() { this.notify("@pushed"); }).bind(this), 0);
          }
        } else if (e.type === "mouseout" || e.type === "touchcancel") {
          flexo.remove_class(this.outlets.button, "__down");
        }
      };

      $_.enable = function(p)
      {
        flexo.set_class_iff(this.outlets.button, "__disabled", !p);
      };

    </script>
    <connect outlet="button" target="b"/>
    <listen dom-event="mousedown" source="b"/>
    <listen dom-event="mouseout" source="b"/>
    <listen dom-event="mouseup" source="b"/>
    <listen dom-event="touchstart" source="b"/>
    <listen dom-event="touchend" source="b"/>
    <listen dom-event="touchcancel" source="b"/>
  </controller>
  <root-view>
    <html:div class="bender-button" id="b"><content/></html:div>
  </root-view>
</component>

The structure of a component is similar to that of an application; actually, an application is just a special case of a component. The main difference is that the top-level element is named component. We have seen that element above when the button component was instantiated in the application; in this context (when the component element does not have a ref or href attribute), we have a component definition rather than an instanciation. Component definitions can occur in separate files as well as inline.

The stylesheet element is used to add style to the component using CSS, either inline or through HTTP (with an href attribute.) Since this is just CSS, this stylesheet provides default styling for the button and can overridden by applications or components that include it.

The root-view is very simple: a simple HTML div element with a Bender content element. This element is a slot inside component views that accepts content from the instantiating component element. This is how the "Thanks" text node from the application above gets rendered inside this button component; the content element having been replaced by the contents of the component element at instanciation.

The controller for this component is a bit more complex. We see six listener elements, which we have seen before; but here in place of an event attribute, we see a dom-event attribute: instead of a Bender event (such as @pushed), we expect a DOM event (such as mousedown.) The source is accordingly a DOM node, the HTML div container for this element. Finally, there is no handler specified; therefore a default handler will be used. The default handler for a controller is (following the DOM) the handleEvent method of the controller delegate object.

The connect element inside controller establishes a connection between the controller (through an outlet named button) to view nodes or components. Here, we want to maintain a connection to the HTML div element representing the button so that we can manipulate its appearance; when the component gets rendered, this outlet will allow us to point to the concrete DOM node in the output tree, that is the actual node that receives events in the client where the application is finally run.

Looking a bit above the connect and listen elements, we see a script element. This element can be used to include additional code, either through an external file (referred to by an href attribute) or inline. A script element appearing inside a controller is called a local script, in the sense that its context of execution is the controller delegate. The special variable $_ gets bound to this object, so that we can add new methods or redefine existing ones, such as the handleEvent method in this case. Conversely, there are global scripts, which, similarly to the stylesheet element can include chunks of Javascript code like the script element of HTML or SVG.

Some points of interest here:

To sum up, this component defines a simple view that can host custom content (not just text, but also an SVG icon for instance.) DOM event listeners are set up to listen to mouse click and touches directed at that container element, which leads visual feedback for the user, and a @pushed event to be generated. Components or applications that include this component can specify content, and set up a single event listener (as opposed to the six event listeners and the non-trivial event handling code) to respond to user interaction with this component. Note that this example is a bit too simplistic and efforts to make this component more accessible are still necessary.

A Non-trivial Application Using SVG

Although the original idea for Bender was the development of a single authoring tool, it quickly evolved into the framework that is described here; and currently, application and component description files are authored by hand. (Hopefully the XML syntax is expressive enough to make this task as painless as possible.) But the idea remains that Bender should be well-suited to build tools as well as end-user applications; as an example of the former, we'll show a simple but actually useful authoring application made in Bender.

The CSS3 Transitions Module includes a transition-timing-function property, which accepts a cubic Bézier curve as value. We built a simple tool to create suitable curves graphically, and generate the corresponding values, as shown in the screenshot below:

Transition Timing
          Function Designer application screenshot

In this application, we mix HTML for the basic layout of the application with SVG for the curve drawing component on the left. This component allows the user to design a curve by clicking and dragging (or touching and dragging, on a touch device) the two solid blue dots, controlling the second and third control points of the curve (the first and fourth being fixed.) Whenever a point is moved, the curve is updated accordingly, and an event is sent to notify the application that the curve data has changed (so that the property value displayed below can be updated as well.) Conversely, when the user wants to try default or random values, by clicking on one of the options on the right, the application receives events from the buttons and tells the curve component to update itself with the new values. Finally, the "Test" button allows the user to test the current set of values by animating two circles over a span of 3 seconds; the orange circle moves along the curve while the blue circle at the bottom moves horizontally following this timing function. Once again, the application controller responds to a pushed event from the test button and invokes the test function from the curve component controller.

In this case, the curve component itself is a complex component: the two curve control points are two instances of another component. A curve-point component, when moving, sends a @moved which causes the curve-component controller to update the drawing of the curve, generating its own @moved event, which is then listened to by the application.

We invite the interested reader to examine the description of this application for more details. We are also adding more sample applications to the Bender website, and the documentation contains a lot of samples and tests, including some SVG specific ones.

Conclusion and Future Work

Bender is an experimental framework that is still under development and whose capabilities are still being investigated. As such, there are many different features left to design or implement, and new directions to explore.

We have described a Web application framework that is by design open to the full array of Web and XML technologies, among which SVG is an integral part and not a second-class citizen. For practical reasons, current development is focusing on a browser environment where HTML is clearly the most convenient rendering tool; however, we are already ensuring that Bender runs satisfactorily on more constrained devices or even in non-browser based SVG players; opening a wide range of possible applications.