Usability is In the Application (UIA)
(Work-in-progress since 2002, by Allan Ebdrup)
This article is intended for web developers. It is a description of some of the
main features of UIA. UIA is my JavaScript
framework for writing entire applications or rich internet applications (RIAs).
I think it is most powerful for developing applications for use within an enterprise.
But can be used for things like my obsurvey application too. It is very strong when
dealing with structured data and documents, as you often do in an enterprise. Also,
in an enterprise functionality is king, it is more important that the application
is powerful and easy to use, and not so important that it is beautiful. UIA has
no fancy effects or animations. It's far from a fancy flash animation, but you
can make
beautiful applications with UIA.
The main features of my framework are:
- Classic inheritance and interfaces
- Type checking
- Obfuscation
- Boxing the types
- Mapped array
- One to many
- HTML components
- Data binding
- HTML array
- Undo support
- Drag and drop
Each feature is described in detail in the following sections.
Classic inheritance and interfaces
Like many other JavaScript frameworks I’ve implemented classical inheritance inspired
by Douglas Crockfords original article. I’ve also added what I call interfaces;
they have definitions of method signatures that you have to implement. My interface
implementation also supports having code in the interface for default implementations
of the methods in the interface, a kind of controlled multiple inheritance. Furthermore
I’ve implemented the possibility to pass arguments when you implement an interface,
you call a myImplements method passing the interface you want to implement and add
the parameters you want to pass. An example of how I use my interfaces, is the way I’ve
implemented drag and drop. See the section on drag and drop for more details. In
drag and drop I use this for a kind of generically typed interface.
Type checking
I’ve implemented type checking on all constructor and method calls. Type checking
is turned off in the production environment, the type checking is not included when
I obfuscate the script for production. I’ve implemented type checking by use of
hungarian notation, so if a parameter is called intCount, the type checking is inserted
to check that intCount is indeed an integer. The type checking is performed on every
method call. I also support allowing parameters to be undefined by suffixing with
a u, uintCount, and I support allowing nulls by suffixing with n, nintCount. Type
checking is also done on types, suppose you have a class called Employee, if you
specify a parameter called objEmployee it will be type checked to be an Employee
object or one of its subclasses.
Obfuscation
I’ve implemented an obfuscator in JavaScript using regular expressions, it can’t
handle all JavaScript, but it can handle all my JavaScript. I use the .replace method
and I obfuscate in one sweep. In FireFox I can obfuscate 430KB of JavaScript in
2 seconds, reducing it’s size to 150KB. I’ve Implemented an environment where I
have an extra menu in the development environment, one of the items on this menu
is obfuscate, I click that and I get a screen where all the JavaScript used in the
application is fetched into one big script. So even though I have a fine grained
include in my development environment, it all gets put together to one script in
production. Then I click obfuscate, get a summary of the compression and click “deploy
to test”. Now I have the obfuscated code running in my test environment, an exact
copy of my production environment. When I have tested there, I click deploy and
the new script is deployed to production.
Boxing the types
I have boxed versions of all the types in JavaScript, and I have extended the base
types with a box method. This allows me to throw events whenever one of the boxed
types is updated. I have a class called ModelEvent that is passed when events are
thrown. For the basic types it is pretty straight forward, there is just an update.
For arrays I support listening to all types of modifications to the array or just
listen to certain kinds of events. I’ve also created an integer boxed type, which
contains a number but checks that it’s an integer. By using a logarithmic search
algorithm I found the number where adding one to the number is rounded off and has
no effect on the integer part. I use this number as the maximum possible integer
value.
Mapped array
The MappedArray class takes a function and a boxed array, the source array, in it’s
constructor. The function is a mapping function. The idea is that the mapped array
always contains the elements of the source array after they have been mapped by
the mapping function. Using the events I get from the boxed source array I update
the mapped array whenever changes are made to the source array. So for example when
a new element is pushed on the source array, the mapping function gets called on
the new element and whatever the mapping function returns is pushed on the mapped
array. I’ve implemented all the manipulations you can make to an array, including
slice, and added some of my own.
MappedArray inherits from the normal boxed array, so with polymorphism you can use
a mapped array anywhere a normal boxed array can be used.
One to many
The OneToMany class takes a boxed array and a pointer in its constructor. The pointer
is either one of the elements in the boxed array or null. When you change the value
of the pointer it has to be an element in the boxed array or null, this is checked.
When changes to the boxed array are made, the one to many automatically checks that
the pointer is still valid. If the pointer is no longer valid, because the element
in the pointer has been removed for the boxed array, the pointer is either set to
null or an error is thrown, depending on how you have configured the one to many.
It’s a bit like foreign keys in databases. The one to many has functions like moveNext,
movePrevious etc. The one to many class also supports listening to changes to the
pointer, like all the boxed types. I have found many uses for the one to many class,
one of the being for dropdowns.
HTML component
Almost everything rendered on the screen I UIA is a HTML component, they inherit
form the HTMLComponent class. Each HTML component has a HTML tag that it is rendered
as, and it supports having a header and footer. So when you addHTMLChild to the
HTML component it is added after the header and before the footer. A HTML component
is almost always a square on the screen, although it can contain absolutely positioned
children. HTML components are really just a proxy to the bare DOM calls. I’ve implemented
my own event handling system and the support swapping and other neat stuff but that’s
it.
Data binding
With the boxed types and the HTML component in place it’s time to build some HTML
components that support data binding. For example I have a class called HTMLInteger.
It takes a boxed integer in its constructor. It renders as a input type text HTML
element. Whenever the box is updated I update the boxed integer automatically, in
short data binding. Because I know the type is an integer I only let keystrokes
that are numbers or minus filter through. I’ve also let the up and down arrows add
and minus one from the number. I’ve created HTML components for all the boxed types
I have, witch is a superset of all the types in JavaScript.
HTML array
A HTML array is used for data binding an array. It is basically just a mapped array
where the mapping produces HTML components. The source array is an array in the
display model that is mapped to a target array of HTML components. HTML array is
a HTML component and inherits from HTMLComponent, so you can add it to your HTML.
All changes to the source array are automatically mapped to HTML components. The
HTML array then maps these changes to operations on the DOM of the HTML page. This
means that newly created HTML components are inserted in the correct place in the
DOM, deleted HTML components are removed from the DOM and swapped elements are also
swapped in the DOM.
The abstraction a HTML array provides is that you just need to have a boxed array
in your display model and a mapping function. When you have changes, you just update
your boxed array in your display model, events are thrown automatically from the
boxed array, the mapping function is automatically called if needed and the HTML
array automatically makes the appropriate changes to the HTML. All the manipulations
you can do to a boxed array are supported by the HTML array.
Drag and drop
To have your HTML component be draggable you simply implement the HTMLDraggableForDropController.
HTMLDraggableForDropController has the default implementation for dragging for drop,
and has the abstract methods getDragForDropArea
and getDragForDropData.
The getDragForDropArea must be implemented to return the HTML component that defines
the area where you can click the mouse, hold the button down and start dragging.
The getDragForDropData method must be implemented to return the data beeing dragged,
the data that will be passed to the HTML component that eventually accepts the drop.
To accept a drop you implement the HTMLDropController. HTMLDropController has the
default implementation for accepting a drop, and has the abstract methods getDropArea
and dropDataHandler.
The getDropArea must be implemented to return the HTML component that defines the
area where you can drop the data.
The dropDataHandler must be implemented to accept the data beeing dropped.
When implementing HTMLDropController you specify what types can be dropped by passing
the classes in the myImplements method. The implementation makes sure your dropDataHandler
is never called with data of types you don’t accept. The implementation supports
polymorphism, so when you accept a class you automatically accept all it’s subclasses.
Undo support
I’ve build in undo functions for the most important boxed types. The boxed types
all inherit from a base class called UndoType. This means that in order to get Undo
support for modifications to your boxed types, all you have to do is set an UndoStack
on the type. If you do this the boxed type automatically registers all modifications
to it and you can undo/redo them by calling the methods undo() and redo() on the
UndoStack.
Because of the databinding capabilities of UIA, the only place you have to worry
about undo’s is in your display model. If you let your display model inherit from
UndoStack and register all your properties you want undo for using the inherited
method addUndoChild(), you don’t need to do anything else to get full undo support.
In practice this means that I could add undo support to obsurvey using just 16 lines
of code in the display model, and the where all either to inherit from UndoType
or addUndoChild(). This was possible because all the data in the entire display
model for obsurvey is expressed as arrays of things and strings, and I used my boxed
arrays and strings.
Please ask questions or leave comments
Other articles by me
I've written an article about undo functionality in web applications entitled Where did undo go?.
Obsurvey uses my Structured Active Rich Document (SARD)
principles, that you can see in action when editing a survey's questions.