Building new Coach View types

The Coach Views supplied out of the box by the product are certainly not the only Coach Views available. Coach Views can be obtained from the IBPM community web sites as-is or you can build your own. When building your own, it is important to understand the architecture associated with Coach Views. We will now look at that architecture in more detail.

The high level notion behind a Coach View is that it will have a visual appearance within a Coach in order to present data to the user, solicit data from the user or improve the look and feel of the Coach page with visual decoration or layout. The Coach View achieves all of this by a combination of technologies including HTML and CSS which is interpreted by the browser, JavaScript for logic inclusion and Dojo (or other UI toolkit) for visual representation.

A new Coach View can be created from the User Interface menu entry and selecting Coach View:

A name must be supplied for the new Coach View. The name entered must be a valid JavaScript identifier.

Once created, the Coach View editor page is shown. This consists of a number of separate tab pages. On the Overview page we have some general settings:

The meanings of the attributes available to be changed are shown in the following table:

Tags
Palette Icon
Layout Image
Use URL binding
Preview Label Position
Can Fire a Boundary Event
Use as a Template
Supports a Label
Prototype-level event handlers

The next page of interest to us in the Behavior page.

This has sections for

Included Scripts
Inline CSS
Inline JavaScript
AMD Dependencies
Header HTML
Event Handlers

AMD Dependencies

Dojo has switched to using the Asynchronous Module Dependencies (AMD) technology to load JavaScript modules into pages. In the AMD Dependencies section, we can name modules and an alias JavaScript variable that will be used to hold a reference to the entry point into that module.

Within the Process Designer web editor, again the corresponding function can be found:

The Variables tab allows us to define data associated with this Coach View.

Amongst these options we will find an entry for defining Configuration Options:

Each configuration option has a variety of properties:

Name Description
Name The name of the configuration option as referenced in the code by this.context.options.<name>
Type - -
Is List A boolean to flag this configuration option as being a list/array of values or a singleton.
Responds to Screen Size This boolean flag is only found in the Process Designer web editor.
Variable Type The data type of this variable. The variable type has a direct bearing on how the configuration property is shown. For example, a Simple Type → Selection will result in a pull-down with the options defined in the selection being the only options.
Label The label to be shown in Process Designer in the Coach designer beside this configuration option
Group Name Here we can supply the name for a "group". Grouped entries will show up in the Coach editor under the same heading.
Documentation Documentation for readability

Here is an example of what a configuration option entry would look like in Process Designer:

Once a Coach View has been created, we can see it in the palette in the Coach Designer.

And from the Process Designer web editor:

The palette is split into groups. When a new custom Coach View is created, it must be tagged as belonging to one or more of these groups.

And from the Process Designer web editor:

If it is not tagged then the Coach View will show up in a section called "No Tags".

Coach View Design Time visualization

When assembling a Coach from Coach Views in Process Designer, we are limited in our visualization. All that is shown in the Coach designer is an icon place holder that represents the Coach View when it is finally shown in Coach at run-time. With the arrival of IBM BPM 8.5.5, this capability was enhanced. When building a Coach View we have a section within the Overview called "Advanced Preview". It looks as follows:

Within this area we can point to managed web files for both a fragment of HTML and a fragment of JavaScript. These combined will be "executed" within the context of the Coach editor within the Process Designer web editor. The purpose of these fragments will be to render the Coach View during Coach assembly.

Within the HTML fragment, some special classes apply:

The JavaScript fragment provides function to programatically control the HTML fragment during design. Within the JavaScript fragment, we define an object and assign that object to a new variable called "mixObject". Within this object we define properties which will be called by the Process Designer web editor at certain points. This allows custom JavaScript code to gain control and perform work.

Some additional housekeeping rules also apply.

Now we can look in more details at the mixObject. This can have a number of functions added to it which will be called by the Process Designer web editor at interesting and important points. IBM has architected what these functions are, their calling times and their parameters.

getHTMLSnippet() – This is called to return the HTML that will be used as the injected HTML snippet. This can be used to encapsulate both the HTML and JavaScript into one JavaScript file. It can also be used to build the HTML that will be used dynamically by the JavaScript itself. If an HTML file has also been supplied, then the content of that file will be available in "this.htmlSnippet".

createPreview(containingDiv, labelText, callback) – This function is called to build the preview that will be shown to the designer. The root of the DOM of the HTML fragment supplied either as an HTML file or returned via getHTMLSnippet() is provided in the "containingDiv" reference. The labelText field contains the initial value of the label. The callback parameter is a function which must be called after the preview has completed its work. This allows asynchronous processing in the preview construction.

destroyPreview() – Destroy / remove the Coach View from the designer.

propertyChanged(propertyName, propertyValue) – Called when a property changes on the visualization of the object. The properties include both the IBM supplied properties and the Coach View specific properties. Included in the IBM supplied properties are:


modelChange(propertyName, propertyValue)

When a callback function defined in the mixObject is invoked, the context ("this") has a property called context that is an object which provides a number of useful attributes.

An example of coding a JavaScript handler might be:

var mixObject = {
 createPreview: function(containingDiv, labelText, callback) {
 // Your code here
 callback();
 },
 propertyChanged: function(propertyName, propertyValue) {
 // Your code here
 }
 };

It isn't uncommon to want to use Dojo within the createPreview function. As such you will want to require any modules you need. For example:

createPreview: function(containingDiv, labelText, callback) {
 require([\<myModule\>], this.lang.hitch(this, function(myModule) {
 // Your code here
 callback();
 }));
 }

Should we wish to store contextual data, we should store it within this.context.coachViewData.

Coach View Event Handlers

During the life of a Coach, each Coach View will be sent certain events over time. When these events arrive, a Coach View can respond to these events in different ways by invoking custom code. When an event happens, a callback function provided by the Coach View can be executed. Six event types are defined by the Coach View architecture. These are:

  • load
  • unload
  • view
  • change
  • collaboration
  • validate

Within the Behavior tab of the Coach View editor, we see a section called Event Handlers. Within there are entries for each of the six different types of event handler. Selecting any one of them gives us an editor area into which we can insert JavaScript code that will be executed when that event occurs.

And from the Process Designer web page:

When code is associated with an event handler, the event handler appears with a check-box mark to indicate that it will "do something":

and in the Process Designer web editor:

Load event handler

The load function is called when the Coach View is initially loaded. This can be used to perform initialization. Experience has shown that this is the most prevalently used handler in Coach View construction.

View event handler

The view function is called just before the Coach View is to be displayed. It is important to note that there appears to be a relationship between the change event handler and the view event handler. This at first seems surprising. What IBM has done is that the default change event handler (i.e. the one that is in effect is not explicitly coded) calls the view event handler when a change event happens. From a logical perspective, this does seem to make sense. If a variable changes, surely the component should update itself (via the view handler) to reflect the new data. However, if one does code up a change handler, the view event handler is not invoked unless explicitly asked for. What trips up developers is the lack of appreciation of this relationship and get surprised that view is called more often than they might otherwise expect.

Unload event handler

The unload function is called before the Coach View is disposed and allows it to perform any final saving or cleanup of resources.

Change event handler

The change event handler is called when data associated with the Coach View changes. This data is commonly either the single data item bound to the Coach or any of the data items set in the Configuration section.

The data structure is available through the variable called "event" which is passed into the event handler. This data structure contains the following properties:

Property name Data type Description
type String An event can occur because of either a change in binding or a change in configuration. If the change occurred because of a configuration change then the type property will have the value "config".
property String The name of the property that changed. Note that many of IBM's configuration properties appear to show up as "_metadata.visibility". There is a belief that this is not an object called "_metadata" with a property called "visibility" but is instead a property actually called "_metadata.visibility".
oldVal N/A The bound value before the change
newVal N/A The bound value after the change
insertedInto Integer
removedFrom Integer

If a new event handler is not explicitly coded, the default change event handler calls the view handler when data changes.

It is important not to try and directly change the value of the property that caused the change callback to fire within a change callback. Experience seems to show that we will enter a loop. Instead, if we need to change the value, register a time based callback that will change the value when the change handler itself has completed its processing.

Validate event handler

This event handler is called when a validation service has been called. The input parameter to the event is either an error or clear object. An error object is used to indicate that the Coach View should show an error while a clear object is used to indicate that a previous error indication is to be removed.

An "error" object has a property called "errors" that is an array of records containing:

Property Name Data Type Description
binding String Path to the variable in error.
message String A description of the error.
view_dom_ids[] String[] A list of DOM Ids that receive the error

A "clear" object has no properties and is used simply to indicate that there are no longer any errors.

How a Coach View should indicate to the user that a validation error has been encountered is a decision left to the designer of that Coach View. It is probably wise to follow the same styles as used by IBM for their supplied views. If we look at the Text view as an illustration of how IBM approached the task, we find that the HTML template for the view contains the following:

<div class="coachValidationDiv"\>
   <img class="coachValidationImg CoachView_hidden" role="alert"\>
   <span class="coachValidationText" style="visibility: hidden;">!</span>
</div>

This basically means that we have an area of the control into which error information can be shown. We see it has two tags. An image tag to show an icon and a span tag into which the error message will be shown.

The high level logic for view update is

if the type of the event is "error"

User Defined Events

In addition to Coach Views having architected life cycle events, there is also the ability to define custom events that can be used to drive programming constructs and other logic exposed out of the Coach View. This is similar to the notion of events found in other UI environments.

Imagine we want to create a new Coach View that has some specialized function. For example, maybe it is a GPS sensor that knows where our mobile device is located on the planet. Our intent is that when the device arrives within 100 meters of a defined locations, we wish to perform a task. It is a good design principle to allow any Coach View we author to be as generally applicable as possible. Rather than hard-code if for the most narrowest of purposes, we should try and consider design options that would maximize its ability to be re-used in circumstances we have never considered. With that frame of mind, we might want an "event" to be fired when the GPS device arrives at its location. We can thus provide an event handler to perform "some task" when the event fires as opposed to hard-coding the Coach View with a specific fragment of logic. This is an example of where the user defined events come into play.

When we define a Coach View we have the opportunity to define the types of custom events that it can support. In the variables tab of the Coach View editor, we find that we can create a new configuration option and give it a name. If we select the "Type" of the option to be "Event" then what we have is a new event definition. The definition also allows us to supply a label and optional documentation.

As soon as we have defined a new event type, we will immediately see a reference to it in the events tab in an actual instance of the Coach View in the Coach editor:

Within the editing section of Process Designer we can insert JavaScript code which is executed when the event is fired against the Coach View. Within the source script we code here, we have access to the current instance of the Coach View through the local variable called "me". For example, we can get the current bound data value using me.getData() or set the current bound data using me.setData(data).

By defining a new custom event in the Coach View definition, it is automatically registered with Coach View instances as a possible event callback. Should we wish, we can explicitly add additional event types without exposing them through the Coach View editor by calling registerEventHandlingFunction(). Using this technique also allows us to specify that an event should accept parameters.

To fire an event against a Coach View, we use the executeEventHandlingFunction().

There is a special event named "eventON_LOAD". If we create an event with that name in our Coach View then it will be fired automatically when the Coach View instance is loaded by the Coach.

Is there also an "eventON_CHANGE"?

The signature of these functions are:

bpmext.ui.registerEventHandlingFunction(this, eventName. <varName1>, … ,<varName9>);

bpmext.ui.executeEventHandlingFunction(this, eventName, <varName1>, …, <varName9>);

Coach View Context

From a programming perspective, the Coach View context is probably the single most important notion. It provides access to a broad variety of information. When JavaScript executes within the browser environment, that JavaScript code can access an object called "this.context" which is a reference to the Coach View context.

We will start by looking at the functions provided by an instance of object.

context.bindingType()

A function which returns the data type of the bound variable.

context.bpm.system

This variable holds a large number of properties that are useful when building Coach Views.


context.bpm.team

This item lists the teams that the user is a member of and which teams he is a manager of. The values are arrays. The objects contains within the arrays contain:


context.broadcastMessage()

context.cancelBoundaryEvents()

context.containDiffLoopingContext()

context.createView(domNode, index, parentView)

context.deleteView(domNode)

context.element

This is the DOM element representing the root of the view in the DOM tree.

context.getDomNode()

context.getInheritedVisibility()

context.getSubview(viewId, requiredOrder)

Get the child Subview with the identity of "viewId". Only one level deep is examined.

context.htmlEscape(<string>)

A function which escapes HTML content and makes it a simple text string.

context.isTrustedSite()

context.parentView()

Returns a reference to the object describing the parent Coach View. If the current Coach View is already on the Coach page itself then it has no parent and null is returned. This method should not be called in the load() function as the environment has not yet been fully built.

context.refreshView()

context.setDisplay(isDisplay, domNode)

This is used to set the visibility of a DOM node. The isDisplay parameter is a boolean. The value true will show the node while false will hide it. The domNode is optional. If not supplied, the root of the DOM tree for the view will be used.

context.setUniqueViewId()

context.subscribeValidation()

context.subview[viewId]

Retrieve the JavaScript object representing the Coach View instance of the immediate child called "viewId". The viewId parameter is the Control Id supplied in the Process Designer editor. Be careful with the objects returned. You really aren't "meant" to do anything with them. About the best you can do is enumerate them to find the viewIds and then call getSubview() to get the actual object.

The following will search the subviews looking for a named view:

function findView(context, viewId) {
 for (var childName in context.subview) {
 if (childName == viewId) {
 return context.getSubview(viewId)[0];
 }
 var result = findView(context.getSubview(childName)[0].context, viewId);
 if (result != null) {
 return result;
 }
 }
 return null;
 }

context.trigger(callback)

When invoked, this method produces the publication of a boundary event. The callback function is optional. If a callback function is provided, it will be invoked with no parameters. The callback occurs when the coach regains control after the asynchronous processing of the event. If we wish to use this API, ensure that the check-box called "Can fire a boundary event" is checked.

context.triggerCollaboration(payload)

context.unsubscribeValidation()

context.viewid

This is the value of the Control Id in the properties view of the Coach Designer where the Coach View is defined:

This can be used as a key value in a number of of algorithms. Take care with tables. It appears that the Control Id/context.viewid may be the same value in each row.

Within a layout page of the Coach View, this is expanded from the value $$viewDOMID$$.

Coach View global data

Within a Coach View, we can also access information about other Coach Views also present in the window. The variable com_ibm_bpm_global provides access to this.

com_ibm_bpm_global.topLevelCoachViews

This is a property which is an object (not a list) which has a property corresponding to each of the Coach Views on the top level canvas. A little extra care appears to be needed when using this variable. It appears that when access from a Coach View's load() function, the variable has not yet been fully populated. This means that even though other Coach Views may be found on the page, they have not yet been added as properties of the page. It appears that by the time the view() method is called, the variable is properly and fully populated.

The following will list all the top level Coach Views:

var x = com\_ibm\_bpm\_global.topLevelCoachViews;
 for (var prop in x) {
 if (x.hasOwnProperty(prop) == true) {
 console.log("Property: " + prop);
 }
 }

com_ibm_bpm_global.coachView.byControlId()

com_ibm_bpm_global.coachView.byDomId()

Coach View data binding and configuration

When a Coach View is added to a Coach, that Coach View can be associated with a piece of data defined as a variable in the Human Service. The association of the coach view to a variable is called data binding. It is highly likely that data binding will be used extensively with most custom Coach Views … after all, a Coach View that doesn't display some configurable data probably isn't that useful.

When a Coach View is displayed in the browser, a copy of the value of that variable is brought from the server and stored as data associated with the Coach View. It is this copy that is used with getters and setters when the Coach View code accesses the data. When a boundary event trigger occurs, the current value of the variable in the Coach View is then sent back to the server and stored in the original variable from which the copy was taken in the first place. This is key. If one uses a Coach View, changes some data and then closes the browser window without first submitting a boundary trigger, the data is not sent back to the run-time environment. In addition, once a Coach View is shown, should there be any changes (somehow) to the values of the variables in the Human Service in the run-time, these changes will not be reflected in the Coach View as it will already have loaded a copy of the original values when it was displayed.

The declaration that a coach view can be bound to data us defined in the Business Data entry of the Variables section:

The value of the data can be obtained from within the code of a Coach View with the call:

var myValue = this.context.binding.get("value");

Note that the "value" parameter is a special keyword and is the actual phrase "value".

When the value of a variable that is bound to a view changes, the view's change() event handler function is invoked to inform it of the change. This is true for simple values such as String and Integer. For Business Objects, the view is notified only if the Business Object itself changes and not if any of its contained properties change. To bind to lower level of a Business Object, we can use the bindAll function.

There is also a bind() function that can name a particular property in the object.

The context.binding JavaScript object has the following properties:

Name Description
property The name of the variable
get A function to get the value
set A function to set the value

Note that a binding does not have to be applied to a Coach View. In the following, no binding has been provided:

What this means is that the context.binding property is not defined. The repercussion of this is that an attempt to access a get() method through this undefined variable/property will cause a failure. As such, it is essential to always check that context.binding is actually defined before attempting to use it. Here is an example:

if (this.context.binding != undefined)
 {
 ...
 }

Working with bound list values provides yet another challenge. A change to an entry in the list if not the same as a change to the list itself and hence no change event is fired. If "myList" is a list variable, then we can provide a callback to be called if an element within the list is updated by using:

var handle = myList.bindAll(function(param), scope);

The "scope" parameter is an optional scope in which the callback function will be executed. The "param" is a JavaScript object that looks as follows:

  • newVal – For an insert, the new value being inserted
  • oldVal – For a removal, the old value being removed
  • insertedInto – The list index into which the item is being inserted for an insert. If no insert or removal has occurred, this property will not be present.
  • removedFrom – The list index from which the item is being removed for a removal

When using binding, one should clean up the binding when finished by calling

handle.unbind();

where handle is the handle returned from a bind call.

In addition to the binding data for variables, a second set of data is also available. Configuration options are options set on the Coach View during development. These are set in the Configuration Options section. Multiple configuration options can be added.

Within the code of the Coach View, the general form for getting a configuration property is:

this.context.options.myOption.get("value");

and for setting a configuration property is:

this.context.options.myOption.set("value", newValue);

The IBM BPM product provides a number of predefined options that are commonly present for all coach views. These include:

Option Data type Description
_metadata.label String The value of the label if one has been provided.
_metadata.visibility String DEFAULT

EDITABLE REQUIRED READONLY NONE HIDDEN | |_metadata.labelVisibility|String|SHOW HIDE | |_metadata.helpText|String|| |_metadata.className|String|| |_metadata.htmlOverrides|Map||

Data types found in IBM BPM are mapped to data types in JavaScript found in the Coach browser environment

Server data type Browser data type
Date/TWDate JavaScript Date

Working with Coach View list data

If a variable is bound to a list, the story changes somewhat. Using the get("value") method returns an object that represents the list. This object has properties and methods upon it which can be used to work with the content of that list. Assume that the variable "list" in the following examples is what is returned from a call to this.context.binding.get("value") of a list bound variable. Do not think of the list object as a JavaScript array. Rather, think of it is a logical container for indexed objects. Do not assume you can perform arbitrary JavaScript like list work against it.

When working with index values, "0" is considered the first element in the list.

Function Description
list.add(item) Adds a new item at the end of the list.
list.remove(index) Removes an item from the list at the specified position.
list.put(index, item) Replaces the item at the specified item in the list. The index must already exist.
list.length() Returns the current length of the list.
list.get(index) Return the item at the index into the list
list.items This is a property of the list object that returns a JavaScript array of the items in the list. The content of the list should not be modified.
list.createListElement() This is an undocumented item that appears to create a new list element but does not add it to the list.

Example: Remove all elements from a list

var myList = this.context.options.myList.get("value");
 while(myList.length() \> 0) {
 myList.remove(0);
 }

In addition to the content of the list, there is also the concept of the selected items in the list. These can be accessed through "gettable" properties of the list object. Make sure that you realize that these are properties of the list object and not of the binding itself.

Property Type Description
listAllSelectedIndices Array of integers An array of integer values. Each integer corresponds to the index of an item in the list which is considered selected.
listAllSelected Array of Objects (read only) An array of objects. Each object corresponds to an object in the list that is considered selected.
listSelectedIndex Integer (read only) The index of the first selected item in the list.
listSelected Object (read only) The object that is the first selected item in the list.

All of the above properties can be read but only the listAllSelectedIndices property may be changed. Setting this to a new array results in a new concept of selected items in the list.

myList.set("listAllSelectedIndices", [\<array of index values]);

If we want to know if these properties change, use the "bind" function. For example:

myList.bind("listAllSelectedIndices", function() {….});

Data Store

The Dojo toolkit has the concept of a "Store". This is a generic container for a list of data. The store concept provides an abstraction to sourcing and sinking data. Since the store has an architected specification, a program should be able to work on the store without knowledge of where the data came from or where it is going. The list data returned in a Coach View also has a "store" associated with it. It can be accessed with the property "dataStore".

Before we get too excited, as of 2013/02, this feature is not documented and hence may not be supported.

If we have a variable and we aren't sure that it represents a list, the following can be used:

if (this.context.binding != undefined && this.context.binding.get("value") != null) {
 var binding = this.context.binding.get("value");
 if (binding.items != undefined && binding.items instanceof Array) {
 // we have an array!!
 }
 }

Dis-allowed Coach Binding data types

Not all IBM BPM data types may be bound to Coach Views, the following are data types that are prevented:

  • XMLDocument
  • XMLElement
  • Map
  • Record

In fact, it is suspected that only the basic data types such as String, Integer, Decimal and Date plus Business Objects that contain these are actually allowed.

IBM supplied Coach View utility functions

IBM has supplied a few utility functions which can be leveraged while developing Coach Views.

Getting URL of managed assets – getManagedAssetUrl

A global function called com_ibm_bpm_coach.getManagedAssetUrl is available which will return the URL of Process App managed assets. This can be used to access assets stored with the Process App. Common examples of usage for this would include accessing images, script files, CSS or data.

The syntax is:

com_ibm_bpm_coach.getManagedAssetUrl(
 assetName,
 assetType,
 *projectShortName*,
 *returnWithoutAssetName*)

The return from this function is the String value of the URL for the asset.

Name Type Description
assetName String The name of the managed asset.
assetType String The type of the managed asset. This must be one of the following values: com_ibm_bpm_coach.assetType_WEB, com_ibm_bpm_coach.assetType_SERVER, com_ibm_bpm_coach.assetType_DESIGN
projectShortName String Optional. The short name (acronym) of the process app or toolkit which owns this managed file.
returnWithoutAssetName Boolean Optional, default is false. Determines whether the returned URL should include the name of the asset or just the path to the asset.

Custom Coach View and position data

As we saw earlier, when we add a Coach View to a Coach, we can set properties in the Position tab of that Coach View instance to control its size and other related attributes. The question before us now, as Coach View designers and authors, is what actually happens when a user sets these values?

First we must realize that when we add a Coach View to the Coach, what is generated is a <div> that represents the Coach View. Even if the Coach View provides nothing further, there will always be this <div>. Each Coach View has a unique Control ID associated with it. When the Coach is rendered, a new CSS Class is created that is given the name of the Control ID. For example, if I create a Coach View instance on the canvas with a Control ID of "MyNewCVInstance" then a new class is created called "MyNewCVInstance". The <div> of the new Coach View has this class added to its class set. When we define the positioning settings, these become the style properties set in the corresponding class. This means that the enclosing <div> of our coach view has an explicit box model set of properties and we can then assume children can size to a percentage of the parent.

The CSS style settings that are changed are:

  • width
  • height
  • min-width
  • min-height
  • padding – This can be be sub-divided into top, bottom, left and right.
  • margin – This can be be sub-divided into top, bottom, left and right.
  • overflow – This is mapped to the "Overflow Content" setting

Generated HTML

When a Coach View is inserted into a Coach within the Human Service editor, the HTML tags that are generated look as follows:

<div id="random_generated" class="??? topLevel CoachView" data-eventid="" data-viewid="controlId" data-config="" data-bindingtype="" data-binding="" data-type="com.ibm.bpm.coach.<UUID>"> … content … </div>

The attributes id and data-viewid seem to be the same and both appear to be the value supplied inside Process Designer within the "Control Id" field:

Experimentation seems to show that data-binding contains the name of the variable in the server but without the "tw" suffix. For example, if the variable bound at the server were "tw.local.xyz" then the value of this HTML attribute would appear to be "local.xyz". This may be the first sign that "tw" (which historically was used for the acronym for TeamWorks) is being removed.

Custom JavaScript in Coach Views

In some cases, you will wish to insert custom JavaScript in your Coach Views. You can insert the code in any of the event handler or in the "In-line JavaScript" section:

It is not uncommon to want to execute the same segment of code as the result of a load event, change event or view event. Replicating the actual code is inefficient. A solution is to create a function that contains that code and hang that function off the View Object which is available as "this" in an "In-line Javascript".

Using JavaScript libraries other than Dojo

The IBM Coach technology is a framework for handling task interactions. When a task is created by a process, the Coach technology (as found in a Human Service) can describe a screen to be shown to the user. The screen is composed of multiple instances of "Coach Views" which are "widgets" or "controls" in other UI languages. The internal architecture of the Coach framework is obviously a combination of HTML, CSS and JavaScript as it runs within the browser. When the browser is asked to "display a coach", the framework for loading and managing the Coach Views as well as providing them data is IBM written logic. Because that logic is written in JavaScript and because browsers are not yet "consistent" in their operations, a JavaScript library is leveraged by the IBM code in order to achieve its task. The library chosen by IBM is "Dojo".

What this means is that the IBM Coach framework has internals for managing the Coach that are written to use Dojo. This requires that some small portions of the Dojo library are required to be loaded into the browser for the Coach to function. There is no customer exposed mechanism for disabling this nor are there any options to replace that code with another JavaScript library.

The inclusion of Dojo for use solely by the Coach framework should not interfere at all with the use of other libraries (eg. YUI) for custom Coach View implementation. The inclusion of Dojo should be transparent.

Using jQuery to build Coach Views

The JavaScript package called jQuery makes a perfectly fine alternative to Dojo as a platform on which to build Coach Views. Here are some notes on using jQuery.

The core jQuery object represents a node in the DOM tree. We can create a jQuery object that represents the root of the CV using:

var jqRoot = $(this.context.element);

There are likely to be many ways in which jQuery can be installed with a BPM environment. Here is my current best recipe:

To create the ZIP on Linux, use:

$ zip -r jqBPM.zip jqBPM

require({ packages: [ { "name": "jqBPM", "location": com_ibm_bpm_coach.getManagedAssetUrl('jqBPM.zip', com_ibm_bpm_coach.assetType_WEB, '<ToolkitName>') + "/jqBPM" } ] });

You have now prepared your environment. To use jQuery in your Coach Views, you can now perform the following recipe:

Although this recipe works just fine, it suffers from the drawback that the Require modules have to be prefixed with a package name. A modification to this technique overcomes that restriction. The solution is to include "paths" section within the require definition. For example:

require({ paths: { "fullcalendar": com_ibm_bpm_coach.getManagedAssetUrl('jqBPM.zip', com_ibm_bpm_coach.assetType_WEB) + "/jqBPM/fullcalendar", "jquery": com_ibm_bpm_coach.getManagedAssetUrl('jqBPM.zip', com_ibm_bpm_coach.assetType_WEB) + "/jqBPM/jquery-2.1.4.min", "moment": com_ibm_bpm_coach.getManagedAssetUrl('jqBPM.zip', com_ibm_bpm_coach.assetType_WEB) + "/jqBPM/moment.min" }, packages: [ { "name": "jqBPM", "location": com_ibm_bpm_coach.getManagedAssetUrl('jqBPM.zip', com_ibm_bpm_coach.assetType_WEB) + "/jqBPM" } ] });

The concept behind the "paths" section is to provide an alias for a named Require module. For example, assume that we wish to require a module called "XYZ". This would (by default) cause AMD to lookup "XYZ" as the file called "XYZ.js" found at the default root of the Require base directory definition. However, for BPM, that defaults to the root of the Dojo distribution which is not where our custom modules are found. Instead, our custom modules exist within a ZIP file added as a BPM Web file. So how then can we cause a reference to "XYZ" to be found within the ZIP? The solution is the "paths" section. Each entry in the "paths" section provides both a module name (for example XYZ) and the full path to a file that will be retrieved when a reference to the module is made. Using the syntax shown in the example, we can thus point our named module to the module source file contained in the ZIP.

To use jQuery mobile. we need to download the jQuery mobile package, extract it and place it in the jqBPM folder, zip that up and add it as a web file to the project.

Generated content in a control

The DOM element in the browser that represents the control can be found by:

var element = dojo.query("*[data-viewid=\"" + this.context.viewid + "\"]")[0];

however, the reference to this DOM node is already set in the variable:

this.context.element

When a Context Box is added to a Coach View, it will be found in the DOM as:

<div … this Coach View instance … > <div … class="ContentBox" …> … Nested Coach Views … … Nested Coach Views … </div> </div>

Making Ajax and REST requests from a Coach View

A Coach View runs within the browser and doesn't have direct access to any server side data. However, IBPM exposes a type of service called an "Ajax Service". An Ajax Service, just like other service types exposes inputs and outputs as either simple data types or as Business Objects. An Ajax Service can be invoked remotely by the Coach View.

Invoking an Ajax Service involves performing a REST request from the coach view to the run-time naming the Ajax Service to be called and passing in the parameters. IBM BPM provides a REST API to invoke Ajax Services and these can be used directly.

An easier solution though is to create a service based configuration parameter on the Coach View. This can have a default Ajax Service named or else can be changed by the user of the Coach View within the Coach editor.

To illustrate how to achieve this effect, here is an example of a new Coach View containing an Ajax call. In the Configuration Options for the Coach View, we define a new option called "targetService". We then define the type of this option to be "Service". Finally, we have the option of defining a default service from the list of services available:

When an instance of this Coach View is added to a Coach within Process Designer, we will find that we have an option available corresponding to the option define the Coach View:

Ok, so far so good. We have now defined a new option on a Coach View which is basically a "reference" to a run-time Ajax Service. Now we need to consider how to invoke an instance of this service.

Within the Coach View implementation, we have the "context" object which has the bound data and configurations associated with it. For each configuration parameter defined, we have:

this.context.options.<optionName>

available to us. Because we have defined an option called "targetService" (in our example), we will find that:

this.context.options.targetService

is a property. We will also find that this is an instance of a function. If we invoke this function, the result will be an invocation of the Ajax Service on the server. That was pretty easy.

so:

this.context.options.targetService();

will invoke the Ajax Service.

We should now consider passing data into and getting data from the Ajax service. Because we are working in JavaScript here, it is assumed that you understand the concept of JavaScript objects.

Each Coach View configuration property that is a service takes a single JavaScript object as a parameter that is passed when the Coach View JavaScript code calls the service. The following fields are defined for this object:


Here is a snippet of code which shows invoking an Ajax Service from within a Coach View:

this.context.options.targetService( { params: { bo1: {"a": "A Value", "b": "B Value"} }, load: function(ret) { console.log("Returned"); }, error: function(ret) { debugger; } } );

As an alternative to making an Ajax call as shown above, we can also make explicit REST requests using the Dojo XHR technology.

REST requests can be made from a Coach View quite easily. The trick is to use the Dojo XHR functions.

Within the code of your Coach View, code the following:

var args = { url: "<target URL>", handleAs: "json", load: lang.hitch(this, function(data) { // Code to process response goes here }) } xhr.get(args);

Two Dojo AMD bindings are used:

Remember that REST requests are asynchronous and that the load function is called at some time later in the processing.

Navigating and access to Coach Views on a Coach page

Consider a Coach shown to the user. The Coach is composed of Coach Views on the page. These Coach Views exist in tree oriented structures.

Let us start with the page itself. At the root of the page, there will be one or more top-level Coach Views. These are the ones added directly to the canvas of the Coach itself. A variable called com_ibm_bpm_global.topLevelCoachViews is an object which contains a property for each canvas top level Coach. Note that com_ibm_bpm_global.topLevelCoachViewsis a JavaScript object and NOT a list.

Given a particular Coach View object, the context.viewid property names the view ID for that Coach View instance.

For a given Coach View, the variable called context.subview is an object that contains a property for each of the Coach Views that it contains. However, be careful. The corresponding property in subview is not the root of the corresponding Coach View. It is somehow related to an HTML DIV in a mysterious way. If we want the Coach View object for the view ID then we should use the method context.getSubview(viewid) which will return an array of real Coach View objects corresponding to the viewid. It is not yet known how there can be more than one element returned since it was believed that at a given level, all Coach View instances had to be unique.

A method called context.parentView() will return us the parent of our current Coach View, assuming that our current Coach View is not already on the base Coach in which case it has no parent.

Here is some example code for walking all the Coach Views in a page:

var showChild = function(parentCoachView, indent) { for (var child in parentCoachView.context.subview) { if (parentCoachView.context.subview.hasOwnProperty(child)) { var childList = parentCoachView.context.getSubview(child); for (var j=0; j<childList.length; j++) { var currentCoach = childList[j]; var pad = ""; for (i=0; i<indent; i++) { pad = pad + " "; } console.log(pad + "> " + currentCoach.context.viewid); showChild(currentCoach, indent + 2); } } } }; for (var child in com_ibm_bpm_global.topLevelCoachViews) { if (com_ibm_bpm_global.topLevelCoachViews.hasOwnProperty(child)) { var currentCoach = com_ibm_bpm_global.topLevelCoachViews[child]; console.log("Canvas > " + currentCoach.context.viewid); showChild(currentCoach, 2); } }

The following additional capabilities can be used:

page.ui.get("<controlId>");

this.ui.get("<controlId>");

See also:


Learning resources to build new Coach Views

The skills required to build new or customize existing Coach Views can be quite broad and varied and includes:


The source of the controls supplied with the product can be examined to understand how they function and how they were put together by IBM. These controls can be found in the Coaches toolkit.

In addition, there are a growing wealth of "as-is" Coach Views available at the IBM BPM Wiki site.

See also:

Debugging Coach Views

While developing Coach Views, it can be useful to use debuggers to see what is going on. Most of the browsers available today have debugging tools built into them including FireFox, IE and Chrome. Using these debuggers we can set break points and examine the content of the DOM tree and Java Scripts.

As an example, if we insert the statement:

debugger;

into a load() function callback and view the coach in Fire Fox with Fire Bug installed, we see the following:

Looking on the right hand side, we can see the value of bindings as well as of configuration options. At the time of writing, my weapon of choice for browser/client side debugging is Google Chrome. Chrome has a built in development tools so no 3rd party plug-ins are needed.

When debugging a Coach View, you may find that the code shown in the debugger appears "compressed" and un-readable. The IBM supplied code, including Dojo is what is called "minified". What this means is that the interpreted JavaScript that comprises the IBM code and Dojo code has been "compressed" by shrinking variable names and whitespace (including line breaks). The reason for this is that by being compressed, it is smaller in size and hence there is less data to be moved over the network when the libraries are being used in the browser. This is a standard technique. However when debugging your own code, it can be confusing. Fortunately, IBM has provided a solution. There is a debugging flag that can be enabled which causes a different version of the JavaScript libraries to be used. That alternate version is not minified and hence does not show compressed in the debuggers.

Here is the recipe to enable the debugging:

Coach View Construction Tips

DOM root access in a Coach View

It is common to include a "Custom HTML" entry in a Coach View so that content may be added to it, a typical example might be:

<div class="MyClass">
</div>

Within the load() event, we would then create DOM content and add it into the DOM model. To find the appropriate entry in the DOM tree, the following code fragment will work:

var myNode = query(".MyClass", this.context.element)[0];

What this code fragment does is search the DOM tree from the DOM node supplied in this.context.element downwards looking for all DOM nodes that have the class "MyClass". We then access only the first one.

Editing JavaScript for Coach Views

Unless the edits are small, it is not recommended to build the code of the callbacks for Coach Views in the Process Designer editors. There is very little assistance here and mistakes can easily happen. Instead, use a professional JavaScript editor with entry assist and copy/paste the code from the editor into Process Designer. You will forget to perform this copy/paste a few times during development but the trade between making that mistake and the errors you will introduce by not using a proper JavaScript editor will more than compensate. I also recommend prefixing your JavaScript code (in the editor) with some dummy definitions for the commonly used functions that will be used in your own code. This will provide the ability to have entry assist.

Adding a label

Many IBM supplied controls have a label associated with them. This label can be shown above the new Coach View.

<div class="textLabel">
   <label class="controlLabel"></label>
</div>

In the load code, we can set the text of the label with:

var labelDom = query("label", this.context.element)[0];
 if (this.context.options._metadata.label != undefined && this.context.options._metadata.label.get("value") != "") {
 labelDom.innerHTML = this.context.htmlEscape(this.context.options._metadata.label.get("value"));
 }

In addition, we will want to handle the visibility settings of the label:

var labelDivDomNode = this.context.element.querySelector(".textLabel");
 if (this.context.options._metadata.label == undefined ||
 this.context.options._metadata.label.get("value") == "" ||
 (this.context.options._metadata.labelVisibility != undefined &&
 this.context.options._metadata.labelVisibility.get("value") == "HIDE")) {
 // hide the label div
 this.context.setDisplay(false, labelDivDomNode);
 } else {
 // show the label div
 this.context.setDisplay(true, labelDivDomNode);
 }

Working with visibility

FIX – This is not current …

The Coach View framework makes available a Visibility concept.

This is set on a Coach View by Coach View instance basis. The values are:


It is up to each Coach View implementation to implement code to honor these settings. One way is to create a handler function in the load:

this._handleVisibility = function() {
 var visibility = "DEFAULT";
 if (this.context.options._metadata.visibility != undefined) {
 visibility = this.context.options._metadata.visibility.get("value");
 }
 if (visibility == "NONE") {
 this.context.setDisplay(false);
 } else {
 this.context.setDisplay(true);
 }
 }

Then we will call this handler in the view function and also when the _metadata.visibility property changes as seen in the change function.

Using a Dijit Widget in a Coach View

When using a Dijit Widget in a Coach View, each widget is expected to have a unique "id" value. Unfortunately, when we place a Coach View in a table, the same "context.viewid" is used for each row in the column and hence the widget can end up with the same id which won't work. Fortunately, Dojo provides an answer. The method

dijit.getUniqueId("<widgetType>")

will generate a unique id. The AMD object to load is:

dijit → dijit

Including a custom Dijit Widget in a Coach View

There are times when you might want to create your own custom Dijit Widget and use that when creating a new coach view. Here is the recipe for achieving that. At a high level, the story progresses by:


We will illustrate by example. Let us imagine that we are creating a new Widget called "DateProgress" that exists in the module called:

kolban/widget/DateProgress

we will assume the following source level tree structure.

kolban
 widget
 DateProgress.js
 templates
 DateProgress.htm

We can export from an Eclipse based environment using the Export to File System:

We now zip this structure up

We add this zip (kolbanWidgets.zip) as a Web managed file to our BPM Process App.

Next we create a JavaScript file (AMDMap.js) that contains:

require({
 packages:
 [
 {
 name: "kolban",
 location:
 com\_ibm\_bpm\_coach.getManagedAssetUrl('kolbanWidgets.zip',
 com\_ibm\_bpm\_coach.assetType\_WEB, '\<ToolkitAcronym\>') + "/kolban"
 }

]
 });

This JavaScript source file then also gets added as a Web managed file. Now we have two managed files. One is the ZIP that contains our Dijit widgets and the other is the JavaScript AMD mapper. In the Coach View included scripts, we reference the AMDMap.js file:

Finally, we can now create a AMD dependency on our new module and map it to a variable.

Describing a Dojo based page declaratively

Within Dojo, we can describe the Dojo widgets to be used either programatically or declaratively. Programatically means that we build the page layout within JavaScript by creating instances of widgets and placing them at the appropriate DOM locations within the DOM tree. This gives us optimal control over placement and parameters but has the significant down-side that we can't use any visual editor to build them out. In addition, modification becomes complex as we have to remember the layout at the program level. All in all, except for the simplest of pages, it is a slow and cumbersome process.

The alternative is known as declarative layout. In this instance, we describe our screen through the use of HTML which can be constructed in a suitable Dojo aware HTML editor such as Worklight Studio or Maqetta. The HTML that describes the Coach View can then be placed in a Custom HTML component within the layout section of the Coach View definition. When the Coach View is loaded, the HTML contained within that area is parsed looking for Dojo declarations. Those Dojo declarations then cause the creation of the associated widgets. At the conclusion, we have a Coach View which contains widgets and looks great.

The power of Coach Views is that they are not just static HTML but contain BPM data and respond to events. We want to then augment the Coach View with JavaScript code that will handle interactions with those widgets. When we created the widgets programatically, we had access to the object that represented those widgets and could add handler and other logic. When we create the description of the page declaratively, we need a little bit more work to get those widget object references.

Dojo provides a class called "dijit/registry" which has a method upon it called "byId(<name>)". This method returns a reference to the Dojo widget on the page that has the corresponding id name. So for example, if we have a button on the Coach View that has an id of "mySubmit", we can gain access to the Dojo widget corresponding to that button with:

var myButton = registry.byId("mySubmit");

There is one final snag with this though and that is the rule that each Widget on the page must have a unique id value. Defining:

<div id="mySubmit"
 data-dojo-type="dijit/form/Button"
 data-dojo-props="label:'Submit'">
</div>

Has a problem. If we have two instances of the Coach View on the page then both buttons will be given the same id and things will break. IBM BPM has a solution to this. If instead we code:

<div id="$$viewDOMID$$_mySubmit" data-dojo-type="dijit/form/Button"
 data-dojo-props="label:'Submit'">
</div>

The special code of "$$viewDOMID$$" is replaced with the DOM ID of the Coach View itself. So if we want to retrieve the button widget, we can now code:

var myButton = registry.byId(domAttr.get(this.context.element, "id") + "_mySubmit");

or we can use the viewId property:

var myButton = registry.byId(this.context.viewid + "_mySubmit");

For any Dojo widgets used in a declarative style, always ensure that you have also used the AMD loader to load the corresponding Dojo implementation of those widgets.

Accessing images by CSS

It is not uncommon to use CSS to access an image. For example, instead of using the <img> tag to show an image, one can use CSS to set the image in a <div>. For example:

.myClass {
 width: 24px;
 height: 24px;
 background-image: url(A URL to an image);
 }

will set the image. The "url" function in the CSS defines where the image is loaded. Unfortunately, there appears to be a puzzle when we build Coach Views. What should the URL be to the image file? We desire that the image be packaged with the Coach View so the first notion of adding the image as a managed Web File seems like it might be sound … however there is a problem. The URL to the image can only be determined programatically. It doesn't have a fixed value. With CSS styling, we don't have access to the JavaScript and hence can't dynamically define the location.

One solution to this puzzle is really quiet elegant. It works as follows:

  1. Build the CSS you want to include in the Coach View. When you need reference to images, supply the URL for those images as file names relative to where the CSS file can be found.
  2. Place the images relative to the CSS file on the file system.
  3. ZIP up the CSS file and the images maintaining the relative directory structure.
  4. Add the resulting ZIP file as a managed web file.
  5. Include the CSS file in the Coach View in the Coach View editor naming the ZIP file and the CSS file contained within.

What happens is that when the CSS is loaded, its reference to the image files will be local to the ZIP file and the server will satisfy those requests from the content of the ZIP.

If you need to update the CSS or image, you must repeat the process which includes packaging up the file system into the ZIP and refreshing the managed web file that corresponds to that ZIP.

Commonly used AMD loaders

When building Coach Views, there are AMD functions that simply keep coming up over and over. Here we capture a cheat sheet for some of the most common:

AMD Alias Example Functions
dojo/query query query
dojo/_base/lang lang lang.hitch
dojo/dom-attr domAttr domAttr.get, domAttr.set

Building a Coach View using a JS file

An interesting technique that one can use for building a Coach View is to create a single JavaScript source file that contains everything one needs in the way of code. By this we mean the definitions for all the callback functions as well. Here is the template we use as the basis.

myControl_xyz = function (bpmext)
{
	this._instance = {
		
	};

	if (!this.constructor.prototype._proto)
	{
		this.constructor.prototype._proto =
		{
			_handleVisibility : function (view)
			{
				var visibility = bpmext.ui.getEffectiveVisibility(view);
				
				view.context.setDisplay(visibility != "NONE");
				view.context.setVisibility(visibility != "HIDDEN");
			}
		};
				

		this.constructor.prototype.getType = function ()
		{
			return "xyz.1";
		}
		
		//
		// Load
		//
		this.constructor.prototype.load = function ()
		{
			try
			{
				var opts = this.context.options;
				var mdt = opts._metadata;
				bpmext.ui.loadView(this);
			}
			catch (e)
			{
				console.log(e);
			}
		};
		
		//
		// View
		//
		this.constructor.prototype.view = function ()
		{
			try
			{

				this._proto._handleVisibility(this);
			}
			catch (e)
			{
				console.log(e);
			}
		};
		
		//
		// Change
		//
		this.constructor.prototype.change = function (event)
		{
			try
			{
				if (event.type == "config")
				{
					switch (event.property)
					{
						case "_metadata.visibility":
						{
							this._proto._handleVisibility(this);
							break;
						}
					}
				}
			}
			catch (e)
			{
			}
		};
		
		this.constructor.prototype.validate = function (event) {
		};
		
		this.constructor.prototype.collaboration = function (event) {
		};
		
		this.constructor.prototype.unload = function ()
		{
			bpmext.ui.unloadView(this);
		};
	}
}

Notice that the code contains prototypes for load, view, change and the other callback functions. What this does is allows us to define all the implementations for the callbacks that we would otherwise have to code in separate pages in process designer in one file. The source file can then be defined as a managed web file to the Process Center. Once done, a few small tweaks in the Coach View are needed.

  • In the Behavior tab, select "Included Scripts" and link to the attached managed file.
  • In the AMD Dependencies, create a Module that is com.salientprocess.bpm.views/bpmext being mapped to bpmext.
  • In the inline Javascript, add myControl_xyz.call(this, bpmext);

Sample Coach Views

Sample Coach View – trivial "hello world"

We start by creating a new Coach View. I called this one "HelloWorld1".

I defined the new Coach View as having a tag of "Control":

This causes the new Coach View to appear in the Controls section of the Coach Editor.

Next, I built an icon that was 16x16 pixels. I used GIMP to create it but you can find many free icons on the Internet. I added this icon into the Files of my Process App as a Web managed file.

By doing this, when the new Coach View appears in a Coach editor, it will have that icon in the palette of available Coach Views. Looking ahead, it will look like:

Next an image was defined for the layout in the Coach Designer editor:

By defining an image here as the layout image, when the Coach Editor is shown, an appropriate place holder will be seen in the editor canvas area when an instance of the Coach View is added to the canvas.

In the Layout tab, a CustomHTML component was added.

In the HTML properties, a small fragment of HTML was added to provide some visualization. When I now create a Coach and add the Hello World Coach View component into the body of the coach and have the Coach display, we end up with an HTML output as desired/expected.

Sample Coach View – Data Change Boundary Trigger

When data changes in a coach view, unless that causes a boundary trigger, no other coach view on the page will be informed of the change. This is not always a good thing. Consider a table that shows a list of customers by region. We might also wish to have a combo box in which we can select the region. If we use the Select coach view, changes to the selection causes nothing else to happen. We can conceivably edit the Select coach view to cause a trigger to occur but this would result in a second coach view for this customized select. Not a good thing. What we want to happen is for a boundary event to fire when the Select changes its data.

Here we examine a way to make that happen. What we will do is build a new Coach View called Data Change Boundary Trigger (a long name … but very descriptive of what it does). This Coach View will have no visual appearance. Instead, what it will do is monitor a data value and, if that data value changes, cause a Boundary Trigger event to fire.

The Coach View is configured to fire a boundary event.

The Coach View has a handler for the "change" event. When the local change event fires, we trigger a boundary condition.

and … that's it. No more complexity than that.

Coach View Templates

When a new Coach View is created, it can be flagged as a Template. What this means is that the new Coach View will not be available for inclusion in Coaches but instead, when future new Coach Views are created they can use the Coach Views flagged as templates as starting places for the new Coach Views.

Coach Views as single source files

An interesting technique for building Coach Views is to create a single source file that represents the complet Coach View. This bypasses the BPM editor for Coach Views but has the benefit of making the resulting code easier to work with from a JavaScript programmer's perspective.

In the following, place it in a source file called Xyz.js. Add that source file as a Web Managed file. Now add this source file as an included script.

In the AMD dependencies, add:

com.salientprocess.bpm.views/bpmext -> bpmext

Finally, in the inline javascript, add:

myControl_xyz.call(this, bpmext);

Here is the source of the Coach View.

myControl_xyz = function (bpmext)
{
   this._instance = {
   };

   if (!this.constructor.prototype._proto)
   {
      this.constructor.prototype._proto =
      {
         _handleVisibility : function (view)
         {
            var visibility = bpmext.ui.getEffectiveVisibility(view);

            view.context.setDisplay(visibility != "NONE");
            view.context.setVisibility(visibility != "HIDDEN");
         }
      };
		

      this.constructor.prototype.getType = function ()
      {
         return "xyz.1";
      }
			
      this.constructor.prototype.load = function ()
      {
         try
         {
            var opts = this.context.options;
            var mdt = opts._metadata;
            bpmext.ui.loadView(this);
         }
         catch (e)
         {
         }
      };

      this.constructor.prototype.view = function ()
      {
         try
         {
            this._proto._handleVisibility(this);
         }
         catch (e)
         {

         }
      };

      this.constructor.prototype.change = function (event)
      {
         try
         {
            if (event.type == "config")
            {
               switch (event.property)
               {
                  case "_metadata.visibility":
                  {
                     this._proto._handleVisibility(this);
                     break;
                  }
               }
            }
         }
         catch (e)
         {
         }
      };

      this.constructor.prototype.validate = function (event) {
      };

      this.constructor.prototype.collaboration = function (event) {
      };

      this.constructor.prototype.unload = function ()
      {
          bpmext.ui.unloadView(this);
      };
   }
}
No Comments
Back to top