Understanding and hacking the browser side of Uniface RIA

Compuware provided with its Uniface 9.4 the RIA stuff through Dynamic Server Pages, working with browser side javascript code making the mapping between the two worlds. This stuff is made of Compuware’s own mini javascript framework which knowns how to talk with Uniface, and with the help of the Dojo Toolkit for the widgets implementation. This article will show what is provided, what can be safely changed, and will give opportunities for widgets modifications.

This article is also available on the udev.info site: http://www.udev.info/uniface/understanding-and-hacking-browser-side-uniface-ria/

uniface webapp directory organization

Let’s start with the webapps/uniface directory. What can we find inside it? Well, the webapp specific directory WEB-INF with the servlet implementation – opaque content. We find two sibling directories : common and css. The later just provide default styles for USP and DSP HTML content. They’re referenced in the layout of the component, and can safely be ignored (you may provide your own default styles).

Then we have the interesting part : common. It’s a common directory for USP and DSP browser execution (though javascript and java applets) and basic resources (some images and css). For USP, there’are some files not interesting us for this article:

  • uniface directory: extracted uwe.cab
  • UGomezTag5.1.js: interface with another product
  • uwe.cab: USP browser side validation
  • webapplet.js: USP applets enabler
  • webext.js: some javascript helper functions
  • websyntax.js: browser side validation stuff All remaining files are for DSP!

The Uniface javascript framework

Uniface sends an HTML document to the browser (on a full page load). This document contains the layout of the top level (not contained in another one) DSP, and a set of javascript commands, which will initiate the whole process. All the data passing through javascript (initial commands and subsequent requests) are then JSON streams, containing the entities/occurrences/fields definitions, and their data.

<html>
<head>
...
</head>
<body>

<!– the html tags as defined in the top level DSP layout –>

<script type=”text/javascript”>
// here UNIFACE initial commands added as the initial JSON stream executed at page load
</script>

</body>
</html>

All javascript files used in RIA are referenced in uweb.ini, which defines the physical webwidgets (which are mapped to logical webwidgets in usys.ini). One specific and pseudo widget is the page one, which is used whenever … a page is loaded.

Do not touch that thing

The files which provide the communication between Uniface and the browser are not too much, and are referenced in the page pseudo webwidget:

  • ubase.js well, start of the framework, with some tools
  • uluv.js fields/entities/components definitions
  • uscope.js triggers access management
  • udatalayer.js commands to make the link between the browser and Uniface
  • usyntax.js syntax checking (not for all types)
  • uwindow.js implements the browser part of [i]webmessage[/i]
  • ubusyindicator.js implements the spinner AJAX

Although you should not touch them, there’re some problems which can be solved with a bit of modification. For example, if you want to change the url or execute javascript from Uniface, you can add a command in udatalayer.js (well, before the 9.5, which will provide what missed). I’ll make another little article about that trick.

The not so mandatory part

The uweb.ini contains also physical widgets definitions. Like forms logical widgets, you can define DSP logical webwidgets. So all remaining files in common aren’t needed for a proper use of Uniface RIA. They’re just there as a set for webwidgets implementation.

Going deeper in widgets definitions

The Uniface datalayer make use of a widget definition interface, or abstract widget, to create with and respond events from. The way widgets are used is by all defined in uweb.ini (yes this file is really important for DSP), provided widgets implement the abstract widget interface.

First, the factory pattern and the abstract interface are defined in uwidget.js. The factory (UNIFACE.widgetFactory) knows how to create registered widgets (with UNIFACE.widgetFactory.addCreator()) implementing an UNIFACE.widget.AbstractWidget.

A third object type can be found in this file : UNIFACE.eventMapper, which maps javascript events to Uniface triggers; obvious, but needed.

From this point, we don’t have any implementation, just abstract (conceptual) definitions. With the default Compuware set of widgets, we then have two families of widgets :

  • simple Uniface widgets (children ofย  UNIFACE.widget)
  • Dojo powered widgets (children of UNIFACE.dijit)

Basic widgets

The first family is used for simple widgets, and non Dojo ones. They’re all defined in uhtmlcontrols.js. We find :

  • plain used for RawHTML, StaticText and FlatButton
  • genericHTML used for AttributeOnly
  • label for Label
  • img for Picture and PictureButton
  • embeddedDSP for DSPContainer (this’s the exception, this one is defined in its own file : udsp.js)

Unused (pre Dojo) widgets

The second set of widgets use the Dojo toolkit, and particulary the dijit subproject. But it seems Compuware’s labs did not use Dojo as a first choice. The uhtmlcontrols.js indeed contains other widget definitions, under the UNIFACE.widget namespace, and providing the (almost) same set of widgets than the Dojo’ed ones. Those widgets are not referenced in uweb.ini, but are usable:

  • input for EditBox
  • multilinebox for TextArea
  • password for Password
  • checkbox for CheckBox
  • button for CommandButton
  • select or [b]dropdownlist[/b] for DropDownList
  • listbox for ListBox

The only two missing, compared to Dojo widgets, are RadioGroup (this one can be developped to use simple HTML controls embedded in a table tag) and DatePicker. Changing uweb.ini permits to make rid of Dojo, and to use initial simple Uniface widgets. I tried, it works! Of course, you loose of benefits from Dojo, but it can be a good starting point if you want to provide your own powered widgets.

Creating widgets

Adding a simple Uniface widget: form

What I really dislike in Uniface made RIA project is a the pure non-sens of not supporting data submission through a button (just like a form’s submit button). In fact, with the original CommandButton, you can’t make a real form submission (pressing the Enter key for example). For this to work, you must:

  • define a form tag, with a [i]javascript action[/i]
  • insert a dummy input with the [i]submit[/i] type (to throw the form submission)
  • insert your command button (visible or not), which click will be simulated by the onsubmit event of the form.
<form action="javascript://" onsubmit="return acme.simulateClick('MYBUTTON.DUMMY.MODEL');">
<!-- form datas -->

<input type="submit" style="visibility:hidden; width:0px;height:0px"/>
<input id="ufld:MYBUTTON.DUMMY.MODEL"/><!-- your command button -->
</form>

Of course, you still have to define the fake acme.simulateClick() function, to find the command button object with a particular id (which is not so simple with Uniface mapping). It would be so simple to have a Form widget, whose Detail trigger would be fired when submiting! In fact, it’s really easy to make one, with a few lines of javascript code.

First, it’s a good habit to define our own namespace. Let’s use ACME. We then create a acme.js containing the following lines :

ACME = {
 widget: {},
}; 

We just define two embedded namespaces: ACME and it’s child widget. Now let’s create the acme.form.js file:


ACME.widget.form = function() {
 UNIFACE.widget.genericHTML.apply(this, arguments);
}

ACME.widget.form.prototype = new UNIFACE.widget.genericHTML();

ACME.widget.form.prototype.doRender = function(aPlaceHolder) {
 this.element = aPlaceHolder;
 this.element.action = 'javascript://';
}

ACME.widget.form.prototype.mapEvent = function(mapper) {
 if (mapper.triggerName === 'detail') { mapper.map(this.element, 'onsubmit'); }
}

UNIFACE.widgetFactory.addCreator('ACME.widget.form', function() {
 return new ACME.widget.form();
}); 

The first function, ACME.widget.form, is the constructor. You never start from scratch, so the first thing to do when creating a widget is to find the proper base class. Here I choose genericHTML (the implementation of the AttributeOnly) because it does not touch the HTML structure, and do not replace the tag content with the widget value. In the constructor, we call the base class’s constructor.

Then we define the new prototype, using the base class object creation as a starting point.

Finally, we have to implement functions, or to override some of the base class. I won’t lie here. You have to study the base class implementation to know what to do. But there’s basic things to know :

The widget is inserted in the document with the render process, which main part is done within the doRender function. UNIFACE widgets tend to use the element field as the related tag object. Here, we don’t touch the original tag, so we define that element is the DOM node passed to the function. As our form won’t submit to an URL, we have to force a javascript URL with the element (form) action.

The form won’t submit to an URL, because we want to fire the Detail trigger of the field. So we have to tell Uniface that the form submission fires the Detail trigger. That’s done with the mapEvent function, which is called for every public web trigger on the field. We say here that the Detail trigger is fired when the onsubmit event occurs on the form tag (the element field).

The last step is to register our new widget to the widget factory (so that Uniface knows how to create our widget with an identifying string).

That’s all! Of course, acme.form.js is not used yet. We’ve to reference it. Let’s go into uweb.ini, and write the following lines at the end:

[aform]
widget=ACME.widget.form
ulayout=TAG=form;HTMLATTRIBUTES=id=ubinding;CHARACTERS=Form
javascript=../common/uwidget.js;../common/uhtmlcontrols.js;../acme.js;../acme.form.js
properties=grp:html_common;grp:style_notextwidget 

This will ensure that when the aform physical widget will be referenced, the following files will be downloaded and evaluated (in order):

  • ../common/uwdiget.js needed for the factory and abstract widget
  • ../common/uhtmlcontrols.js for the genericHTML widget
  • ../acme.js for our base namespace
  • ../acme.form.js – our new widget

The section also inform the IDE about the available widget properties and what to insert in the layout for the Copy as HTML action.

Now in usys.ini, we define a Logical Widget in the [webwidgets] section:

; ACME widgets AForm=aform 

Restart the IDF, and add a new field in your structure. Choose AForm as widget type, copy as HTML, and fill with a submit button and other fields:

<form id="ufld:FRM.DUMMY.MODEL">
 <input type="text" id="ufld:A.DUMMY.MODEL"/>
 <input type="text" id="ufld:B.DUMMY.MODEL"/>
 <input type="checkbox" id="ufld:C.DUMMY.MODEL"/>
 <input type="radio" id="ufld:D.DUMMY.MODEL"/>

 <!-- I've added the submit and reset buttons which will control the form -->
 <!-- note that they're not Uniface field, just plain HTML inputs -->
 <input type="submit" value="Submit"/>
 <input type="reset" value="Reset"/>
</form> 

Compile, test, and enjoy!

Adding a Dojo widget

Uniface comes with several Dojo widgets from a predefined set. All included Dojo widgets aren’t mapped to Uniface, so it’s possible to add Dojo widgets just adding a few javascript code lines. For our example, I’ll choose one user experience attractive widget: the TitlePane. This widget offers a way to show/hide a content clicking on a title. We’ll also add a new property to manage the hiding/showing effect duration.

A Dojo’ed Uniface widget is defined the same way classic HTML widgets are, but with an improved base prototype. The widget must map the Uniface field value to the visibility state of the widget, and react to mouse clicks. Here’s the definition:


ACME.widget.titlepane = function() {
 // ensure the correct .js files are loaded
 dojo.require('dijit.TitlePane');
 UNIFACE.dijit.apply(this, arguments);
 // used when the widget is created
 this.controlClass = dijit.TitlePane;

 // this internal function controls the Dojo TitlePane effect duration
 this.setDuration = function(aValue) {
 if (this.control._wipeIn) { // ensure this's available
 // set for both showing/hiding
 this.control._wipeIn.duration = aValue;
 this.control._wipeOut.duration = aValue;
 }
 }
}

ACME.widget.titlepane.prototype = new UNIFACE.dijit();

ACME.widget.titlepane.prototype.setValue = function(aVal) {
 if (this.control) {
 try {
 var l_val = aVal;
 if (typeof(aVal) == 'string') {
 // convert to bool
 l_val = (aVal.match(/^[on|yes|y|true|t|1)$/i) != null);
 }
 // update current state
 this.control.attr('open', l_val);
 }
 catch(e) {
 }
 }
}

ACME.widget.titlepane.prototype.getValue = function() {
 if (this.control) {
 return this.control.open ? "1" : "0";
 }
}

ACME.widget.titlepane.prototype.setHtmlProp = function(aProp, aValue) {
 if (this.control) {
 try {
 // the 'title' attribute is the pane's title
 if (aProp === 'title') {
 this.control.attr('title', aValue);
 }
 }
 catch (e) {
 }
 }
 // continue with other properties
 UNIFACE.dijit.prototype.setStyleProp.apply(this, arguments)
}

ACME.widget.titlepane.prototype.setUnifaceProp = function(aProp, aValue) {
 // we use a 'duration' property in the Uniface field properties category
 if (aProp === 'duration') {
 this.setDuration(aValue);
 }
}

ACME.widget.titlepane.prototype.doRender = function(aPlaceHolder) {
 UNIFACE.dijit.prototype.doRender.call(this, aPlaceHolder);
}

ACME.widget.titlepane.prototype.mapEvent = function(mapper) {
 if (mapper.triggerName === 'onclick') {
 // react to title bar clicks
 mapper.map(this.control.titleBarNode, 'onclick');
 }
}

UNIFACE.widgetFactory.addCreator('ACME.widget.titlepane', function() {
 return new ACME.widget.titlepane();
}); 

We then add the definition in uweb.ini:

[atitlepane]
widget=ACME.widget.titlepane
ulayout=TAG=div;HTMLATTRIBUTES=id=ubinding;CHARACTERS=TitlePane
javascript=../common/uwidget.js;../common/dojo/dojo.js;../common/dojo/dojo-for-uniface.js;../common/udojo.js;../acme.js;../acme.titlepane.js
css=../common/dijit/themes/tundra/tundra.css
properties=grp:html_common;grp:style_textwidget;grp:style_box1;grp:style_table;grp:style_visibletext;grp:uniface_all;duration 

Note the duration property in the properties field. We’ll declare this new custom property in uproperties.ini, adding at the end:

[duration]
description=Animation Duration
datatype=real_number
tooltip=Duration in milliseconds of various Dojo widget animations
category=Uniface 

And we can now add a logical widget in usys.ini:

; ACME widgets
ATitlePane=atitlepane(theme=tundra) 

If we now create a DSP, with a field TP with the following characteristics:

  • – Widget: ATitlePane
  • – Datatype: boolean
  • – Interface: B

And paste it in the HTML layout, we have our titlepane. All elements contained between the opening and the closing tags will be affected by the pane state. For example:

<div id="ufld:TP.PAGE.DUMMY">
 <!-- inner content will be shown/hidden depending on TP.PAGE.DUMMY value -->
 <span id="ufld:ANOTHER_FIELD.PAGE.DUMMY"></span>
 <span>classic content</span>
</div>

Let’s initialize our pane to hidden at startup, and set an effect duration of 500 milliseconds:

public web

; title pane is initially hidden
tp.page.dummy = 0

; set its title
putitem/id $fieldproperties(tp.page.dummy), "html:title", "TitlePane's title"

; change default effect duration
putitem/id $fieldproperties(tp.page.dummy), "duration", 500 

And why not another framework?

The Uniface framework is composed of several parts, which are in fact distincts, as long as a common interface is respected. This’s true for widgets: they all derive from UNIFACE.AbstractWidget and make use of other UNIFACE namespace tools, but beside that, there’s no coupling between Uniface and Dojo. So, by reimplementing the widgets, we could use another toolkit, or another Dojo version.

Conclusion

This contribution, while quite long, is far from complete. The subject is very interesting and many details were omited for the sake of simplicity (and reading time). With a good understanding of Uniface javascript framework and how Uniface generates and reads its JSON data coupled with the HTML layout, one can even define it’s own framework.

The Uniface RIA jingle is develop RIA applications in Uniface without HTML nor javascript knowledge. Well, for samples or really simple applications, it’s true. But for a full user experience, extensions must be created. The force of Uniface is its smooth integration with any technology – just see the web 2.0 as another integrated technology. We just have to beautify it !

Note on 9.5

As of 2011, may 10, Compuware released a controlled release of Uniface version 9.5, which provides a new javascript API. This API tends to show that customers behavior extensions can’t be bypassed. We, as developers and solution architects, need flexibility, to overcome provided defaults limitations. The new javascript API is a key to a clean way to extend Uniface RIA possibilities, whereas evil hacks had to be made on Compuware’s javascripts before.

0 thoughts on “Understanding and hacking the browser side of Uniface RIA”

  1. Hi Richard, Thank you very much for sharing this article with us. As you mentioned, it is a necessity to give us, the customers the possibility to extend the widgets (and frameworks) without placing a wish. Think the same is necessary for the C/S part where we wait for a documentation of the OWI (OpenWidgetInterface) which was available in Version7. This would give us a chance to wrap a grid widget “from the market” wich all the features one need and handle it just like the current one. ****** At the end, this extensibility is the key for success if you see all the wishes about extending widget functionality waiting for years, not beeing answered at all. Uli

  2. Hi Richard and others,

    although I realize and respect your need to customizing the framework to suit your needs (great job in doing so :-), from an official point of view I must warn you. Although the framework is delivered as readable JavaScript, it does not make them public and it it is not allowed to make any changes to them. Even making extensions based on definitions created by our JavaScript is risky. We might (and probably will) make changes to the JavaScript files breaking any changes or extensions you might have made to them.
    We cannot prevent you from doing so, but make sure to reserve time when migrating to a newer version. As long as it works, it works.

    The JavaScript API as delivered with Uniface 9.5 is an official API and will be supported by the Uniface Lab.

    In the meanwhile, have fun hacking ๐Ÿ˜‰

    Gerton

  3. Hi Gerton, I respect your official comment on that matter. *** But if you look at the wishlist, there are a lot of demands for other or better widgets *** Richard and others do not hack for the sake of hacking, but for the need to extend the widget collection to meet their customers requirements. AFAIK, there is not so much done incorporating wished widgets to the uniface-wrapped collection (C/S as well as web). *** Wouldn’t it be better to document the wrapping procedure like OWI was documented in version7 to take all these wishes way from “the Lab”?. Uli

  4. Hi all, indeed my widget extensions do not work with the 9.5 C1 (the event system changed). As said Ulrich, what is missing is a clean and stable widget interface to avoid hacking (the other hacking side should not be needed anymore with the new javascript API) – I would like to see the OWI back too ๐Ÿ™‚ – Without some modifications we made in the framework (and not presented here), our web-migrated product would not be on the market. When we discover the Uniface RIA, it was more a brain-teaser than a funny thing, to hack Uniface to make it do what we wanted. The labs go the right way, so continue and provide an official javascript widget interface. Richard o_/

Leave a Reply