OpenAjax Metadata 1.0 Specification Widget APIs

From MemberWiki

Jump to: navigation, search

NOTE: This wiki page holds part of the OpenAjax Metadata 1.0 Specification.


<--previous       contents--^       next-->


Contents

Chapter 4: Widget APIs

Introduction

This chapter defines the widget APIs that enable OpenAjax Widgets to interact at runtime with run-time environments and other widgets.

To introduce the APIs, here is the XML metadata for a sample OpenAjax Widget that shows the current time:

time_oam.xml

<?xml version="1.0" encoding="UTF-8"?>
<widget xmlns="http://openajax.org/metadata" 
  id="http://openajax.org/metadata/spec/samples/widgets/api/RandomNumberSubscriber"
  name="RandomNumberSubscriber" version='1.0' spec='1.0'
  jsClass="OpenAjaxSamples.RandomNumberSubscriber">

  <!-- The widget's class is defined inline within this <require> element. -->
  <require type="javascript"><![CDATA[

    // If OpenAjaxSamples object doesn't exist, create it.
    if (typeof OpenAjaxSamples== "undefined") OpenAjaxSamples= {};

    // Provide a null constructor function for the class identified by 'jsClass' attribute
    OpenAjaxSamples.RandomNumberSubscriber = function() {}

    // Provide an onLoad callback function on the widget instance object
    // The onLoad function is invoked once the widget is loaded and ready to render
    OpenAjaxSamples.RandomNumberSubscriber.prototype.onLoad = function() {

      // Subscribe to topic "OpenAjaxSamples.RandomNumber"
      this.OpenAjax.hub.subscribe("OpenAjaxSamples.RandomNumber", function(topic, data) {
        // This is the callback function that is invoked whenever an
        // "OpenAjaxSamples.RandomNumber" message is received.
        if (typeof data === "number") {
          // Update the presentation to show the random number.
          document.getElementById("__WID__RandomNumber").innerHTML = data;
        }
      });
    }
  ]]></require>
    
  <content><![CDATA[
    <div style="border:2px solid; background-color:lightyellow; padding-left:1em;">
      <h4>Subscriber widget</h4>
      <p>Random number received was: <span id="__WID__RandomNumber">(nothing received yet)</span></p>
    </div>
  ]]></content>

  <!-- This widget subscribes to topic "OpenAjaxSamples.RandomNumber". -->
  <topic name="OpenAjaxSamples.RandomNumber" type="Number" subscribe="true"/>
</widget>    

Here are some of the key things to notice in the example above:

  • 'jsClass' attribute - Any widget that requires the use of the Widget APIs defined in this chapter must include a jsClass attribute on its <widget> element. By providing a 'jsClass' attribute, the widget metadata is telling the developer tool that this widget is a "mashable widget" that requires support for the Widget APIs defined in this chapter. The value of the 'jsClass' attribute is the name of the JavaScript class for the widget. For mashable widgets which do not have their own JavaScript class, the 'jsClass' attribute may be set to 'Object'. In the example above, the jsClass is OpenAjaxSamples.RandomNumberSubscriber.
  • Class constructor and class methods - The example above shows typical JavaScript approaches for defining a class, such as a constructor function (i.e., OpenAjaxSamples.RandomNumberSubscriber = function() {}) and class methods defined on the class prototype (e.g., OpenAjaxSamples.RandomNumberSubscriber.prototype.onLoad). At runtime, the mashup container application will invoke the class constructor to create an instance of the widget class (in this case, an instance of OpenAjaxSamples.RandomNumberSubscriber).
  • OpenAjax Widget APIs - The various Widget APIs defined in this chapter will be mixed into the widget instance object on the OpenAjax sub-object.
  • OpenAjax Hub Publish/Subscribe APIs - The publish/subscribe APIs from OpenAjax Hub 2.0 (HubClient interfaces) will also be mixed into the widget instance object, and attached to the OpenAjax.hub sub-object. Therefore, to subscribe to the OpenAjaxSamples.RandomNumber message that might be sent from other widgets, the JavaScript logic invokes this.OpenAjax.hub.subscribe("OpenAjaxSamples.RandomNumber", ...);.

Developer tools that support the Widget APIs defined in this chapter MUST do the following:

  • For each widget instance, the run-time environment MUST invoke the constructor for the JavaScript class identified by the 'jsClass' attribute on the <widget> element
  • Attach an OpenAjax object as described in this chapter
  • Attach an OpenAjax.hub object as described in this chapter
  • As run-time events occur, invoke the appropriate callback functions that the widget developer places onto the widget instance object

Some of the APIs defined within this chapter use JSDoc commenting conventions to describe various details about a particular API, such as a method's parameters, return values and exceptions. For details about the subset of JSDoc used in this specification, refer to the write-up on JSDoc Conventions within the Introduction chapter.


<Widget> interface

The 'jsClass' attribute on the <widget> element identifies the JavaScript class that provides the Widget APIs defined in this chapter. Within this chapter, the various APIs on this JavaScript class are called the "<Widget> interface" (i.e., "<Widget>" is used in place of the actual jsClass name).

The widget developer MUST ensure that there is a constructor function available for the JavaScript class identified by the 'jsClass' attribute.

All other APIs are optional callback functions that the widget developer might provide. Developer tools MUST check for the presence of these callback functions and only attempt to invoke the callback functions if they are available on the widget instance object. For example, if the widget changes size, the run-time environment MUST only attempt to call the onSizeChanged() callback function if such a function exists on the widget instance object.

The various callback functions on the <Widget> interface listed below SHOULD NOT throw exceptions.

Extensibility notes

Implementers of this specification might find the need to pass implementation-specific function parameters to some of the functions described in this chapter. In order to avoid conflict with future versions of the OpenAjax Metadata Specification and other software that might extend the Hub, implementation-specific parameters SHOULD be attached to an appropriate object parameter using a sub-object, where the sub-object's name is unlikely to collide with other implementations. One recommended technique to avoid collisions is for the name of the sub-object to reflect the name of the organization associated with the parameters on the sub-object.

For example, lets assume that ABC.org is an implementer of this specification and needs to pass implementation-specific data to the <Widget> onChange callback. The implementation-specific data could be passed on an ABCorg sub-object to the event parameter:

var event = { 
  property:"myProperty",
  oldValue:"foo",
  newValue:"bar",
  self:false,
  ABCorg: { 
    /* implementation-specific data */ 
  } 
}

<Widget> Constructor

The widget's class constructor (i.e., the 'jsClass' attribute on the <widget> element) is defined as follows:

/**
 * @constructor
 * Each widget must have a JavaScript class, and that class must have a constructor. 
 * The constructor has no arguments.
 */
// <Widget> = function() {};

<Widget>.onLoad

The run-time environment MUST invoke the onLoad callback function (if available) once the widget has finished loading and all resources specified by <library> and <require> elements are available.

/** 
 * OPTIONAL FUNCTION
 * 
 * This event signals that the widget has finished loading and that all 
 * resources specified by <library> and <require> elements are available. 
 *
 * This is NOT the same as the browser's window or page onload event.
 * 
 * The callback function has no arguments.
 */
// <Widget>.prototype.onLoad = function() {};

Widget developers SHOULD NOT directly register browser onload event handlers (e.g., window.addEventListener('load',myCallback,0);) so that the widget is portable across many different scenarios, including dynamic run-time scenarios where the widget might be added to the document after the browser's 'load' event has occurred. Instead, widgets SHOULD rely on the onLoad callback defined in this specification for notification that the widget has finished loading and is ready for rendering.

<Widget>.onUnload

The run-time environment MUST invoke the onUnload callback function (if available) when the containing page is about to unload the widget.

/** 
 * OPTIONAL FUNCTION
 * 
 * This event signals that the page is about to unload the widget. 
 * This notification is intended to allow the widget to store any 
 * transient data such that it can be recovered upon reloading.
 *
 * This is NOT the same as the browser's window or page onunload event.
 * 
 * The callback function has no arguments.
 */
// <Widget>.prototype.onUnload = function() {};

Widget developers SHOULD NOT directly register browser onunload event handlers (e.g., window.addEventListener('unload',myCallback,0);) so that the widget is portable across many different scenarios, including dynamic run-time scenarios where the widget might be removed from the document much earlier than the page is unloaded. Instead, widgets SHOULD rely on the onUnload callback defined in this specification for notification that the widget is being removed from the page.

<Widget>.onChange

The run-time environment MUST invoke the onChange callback function (if available) after ANY property value changes.

/** 
 * OPTIONAL FUNCTION
 * 
 * If the onChange callback exists, it is invoked after ANY property 
 * value changes. This allows a developer to have a single callback 
 * for any property change events.
 * 
 * @param {Object} event
 * 		The event payload is an object that has the following properties:
 * 
 * @param {String} event.property
 * 		Property's name (same as 'name' attribute on <property> element)
 * @param {String} event.oldValue
 * 		Any JSON-serializable JavaScript value (e.g., String or Object)
 * @param {String} event.newValue
 * 		Any JSON-serializable JavaScript value (e.g., String or Object)
 * @param {Boolean} event.self
 *              'true' if this widget changed the property value by calling
 *              setPropertyValue(). 'false' otherwise.
 */
// <Widget>.prototype.onChange = function(event) {};

The run-time environment MUST invoke the onChange callback whenever a property value changes, even if this widget caused the property value to change by calling the setPropertyValue function itself.

<Widget>.onChange<PROPNAME>

The run-time environment MUST invoke the onChangePROPNAME callback function (if available) after the value of property PROPNAME changes. For example, if the 'date' property's value changes, and this widget supplies an onChangeDate callback function, then the run-time environment MUST invoke the onChangeDate callback function.

/** 
 * OPTIONAL FUNCTION
 * 
 * OnChangePROPNAME event, where PROPNAME is the property's name
 * with the first letter capitalized, e.g. onChangeDate. 
 *
 * This property-specific callback function allows a developer to 
 * have different callbacks for different properties. 
 *
 * If the onChange<PROPNAME> callback exists, it is invoked after a
 * property called <PROPNAME> property value changes. 
 * 
 * @param {Object} event
 * 		The event payload object is the same as for onChange()
 */
// <Widget>.prototype.onChange = function(event) {};

See the description of the onChange callback function for additional information.

<Widget>.onModeChanged

The run-time environment MUST invoke the widget's onModeChanged callback function (if available) immediately after the widget's display mode has changed.

/** 
 * OPTIONAL FUNCTION
 * 
 * This event signals that the mode for the widget has changed. 
 * 
 * @param {Object} event
 * 		The event payload is an object that has the following properties:
 * 
 * @param {String} event.oldMode
 * 		"view"|"edit"|"help"|<QName>
 * @param {String} event.newMode
 * 		"view"|"edit"|"help"|<QName>
 * @param {String} event.renderedBy
 *		"host" | "widget"
 */
// <Widget>.prototype.onModeChanged = function(event) {};

The run-time environment MUST invoke the widget's onModeChanged callback function (if present) whenever the current widget display mode changes. (See the write-up on the 'mode' attribute on the <content> element.)

At widget load time, the widget's JavaScript logic can invoke the getMode() function to determine the widget's initial display mode.

The renderedBy property on the event object indicates whether the rendering of the new display mode will be done by run-time environment logic (i.e., when renderedBy is "host") or by using one of the widget's <content> elements (i.e., when renderedBy is "widget").

<Widget>.onSizeChanged

The run-time environment MUST invoke the onSizeChanged callback function (if available) immediately after the widget's size changes.

/** 
 * OPTIONAL FUNCTION
 * 
 * This event signals that the widget has just been resized by the host. 
 * 
 * @param {Object} event
 * 		The event payload is an object that has the following properties:
 * 
 * @param {Number} event.oldWidth Previous width in pixels.
 * @param {Number} event.oldHeight Previous height in pixels.
 * @param {Number} event.newWidth New width in pixels.
 * @param {Number} event.newHeight New height in pixels.
 */
// <Widget>.prototype.onSizeChanged = function(event) {};


<Widget>.onRefresh

The run-time environment SHOULD invoke the onRefresh callback function (if available) whenever the widget needs to redraw itself.

/** 
 * OPTIONAL FUNCTION
 * 
 * This event signals that the container considers the widget's presentation
 * to be stale and requires a refresh. 
 *  
 * The callback function has no arguments.
 */
// <Widget>.prototype.onRefresh = function() {};


<Widget>.OpenAjax object

When a widget is instantiated, the run-time environment MUST place an OpenAjax sub-object onto the widget instance object and MUST provide implementations of the various methods described in the sections below.

<Widget>.OpenAjax.getId()

The getId() function returns the string corresponding to this widget's __WID__ substitution variable. (For more information about this variable, see Widget ID substitution in the Variable Substitution chapter.)

/**
 * Returns the ID string corresponding to this widget
 * (i.e., the value of the __WID__ substitution variable).
 * 
 * @returns 
 * 		ID string corresponding to this widget
 * @type {String}
 * 
 * @throws {OpenAjax.widget.Error.Inactive} If widget is not currently active.
 */
<Widget>.OpenAjax.getId = function() {};

<Widget>.OpenAjax.getAvailableSize()

The getAvailableSize() function returns the maximum size in pixels that the run-time environment will allow this widget to grow.

/**
 * Return the maximum size in pixels that the run-time environment will
 * allow this widget to grow. 
 * 
 * @returns 
 * 		The return value is an object with a width and/or a height 
 * 		property representing the current available dimensions of  
 *              the widget.
 * @type {Object}
 * 
 * @throws {OpenAjax.widget.Error.Inactive} If widget is not currently active.
 */
<Widget>.OpenAjax.getAvailableSize = function() {};


The size refers to the element that contains the widget inside the container. If an iframe container is used and the iframe window contains a DIV, the returned dimensions refer to the DIV (and NOT the iframe or the iframe's parent element).

The returned object specifies the dimensions of the widget that the mash-up is willing to permit. If the run-time environment does not return a width or a height, then the widget may ask for whatever size it would like when using the requestSizeChange method.

For example, if a widget must be at most 500 pixels wide but the framework does not constrain the height, this function would return the object:

{ width: 500 }

Often, the run-time environment will mirror size constraints to the widgets, so there may be no need to send a request to the manager application and instead this function might return immediately.

Widget developers should be cautious. If getAvailableSize is called at roughly the same time as other widgets are being resized, the information that it returns may be out of date. This information should therefore be regarded as a hint.


<Widget>.OpenAjax.getSize

The getSize() function returns the widget's current size in pixels.

/**
 * Request the current width and height of the 
 * element that immediately contains the widget. 
 * 
 * @returns 
 * 		The return value is an object with a width and a height 
 * 		property representing the current dimensions of the widget.
 * @type {Object}
 * 
 * @throws {OpenAjax.widget.Error.Inactive} If widget is not currently active.
 */
<Widget>.OpenAjax.getSize = function() {};

If an iframe container is used and the iframe window contains a DIV, the returned dimensions refer to the DIV (and NOT the iframe or the iframe's parent element).

For example, if a widget's size is currently 500px x 400px, this function would return the following object:

{ width: 500, height: 400 }

Often, the run-time environment will mirror size information locally on the widgets, so there may be no need to send a request to the manager application and instead this function might return immediately.

Widget developers should be cautious. If getAvailableSize is called at roughly the same time as other widgets are being resized, the information that it returns may be out of date. This information should therefore be regarded as a hint.

<Widget>.OpenAjax.requestSizeChange

The requestSizeChange() function requests that the widget be resized.

/**
 * Request that the run-time environment update the
 * width and height of the element that contains the widget. 
 * 
 * @param {Object} dimensions
 *         This parameter is an object with width and/or height properties.
 *         For example:
 *         { width: 500, height: 400 } requests that the height be reset 
 *              to 500px and that the width be reset to 400px.
 *         { height: 400 } requests that the height be reset to 400px
 *              and that the width be left alone.
 *         { width: 500 } requests that the width be reset to 500px
 *              and that the height be left alone. 
 * 
 * @throws {OpenAjax.widget.Error.Inactive} If widget is not currently active.
 * @throws {OpenAjax.widget.Error.BadParameters} Incorrect function parameters.
 */
<Widget>.OpenAjax.requestSizeChange = function( dimensions ) {};

The requested size refers to the size of the immediate parent of the widget. Thus, if an iframe contains a DIV that contains the widget, the returned dimensions refer to the DIV (and NOT the iframe or the iframe's parent element).

Various results are possible. The widget adapter/mash-up might:

1. Change the dimensions to the requested size, OR

2. Change the dimensions, but to a size different from the requested size (usually smaller in one or both dimensions, based on space constraints), OR

3. Not change the current dimensions at all.

If the size does change, the widget will be notified of the change via an sizeChanged event.

Widget developers SHOULD NOT call requestSizeChange() within the onSizeChanged callback function to avoid the possibility of an infinite loop.

If the run-time environment does NOT change the widget size in response to an requestSizeChange() request, then the run-time environment MUST NOT fire a sizeChanged event and therefore MUST NOT invoke the onSizeChanged callback function.


<Widget>.OpenAjax.getMode

The getMode() function returns the widget's current display mode. (See the write-up on the 'mode' attribute on the <content> element.)

/** 
 * Returns the widget's current mode.
 * 
 * @returns mode
 * 		The widget's current mode
 * @type {String}
 *
 * @throws {OpenAjax.widget.Error.Inactive} If widget is not currently active.
 */
<Widget>.OpenAjax.getMode = function() {};

If getMode() is invoked from within an onModeChanged callback, the run-time environment MUST return the new mode, not the old mode.

<Widget>.OpenAjax.requestModeChange

The requestModeChange() function requests that the widget switch to a different display mode. (See the write-up on the 'mode' attribute on the <content> element.)

/** 
 * Request transition to a specified mode. The widget container might
 * not honor this request. If the request is honored, then the onModeChanged
 * event will notify the widget of the change (if the widget has a handler for
 * that event).
 * 
 * @param {String} mode
 *      The name of the mode to which the widget is requesting transition
 *
 * @throws {OpenAjax.widget.Error.Inactive} If widget is not currently active.
 * @throws {OpenAjax.widget.Error.BadParameters} Incorrect function parameters.
 */
<Widget>.OpenAjax.requestModeChange = function( mode ) {};


<Widget>.OpenAjax.getPropertyValue

The getPropertyValue() function returns the current value of a particular property. (For more on properties, refer to the Properties chapter.)

/**
 * Returns the current value of the requested property name. 
 * 
 * This function returns immediately.
 * 
 * @param {String} name	 
 * 		The name of the property to retrieve
 * 
 * @returns 
 * 		The current value of the property named name. 
 * 		The value must be serializeable as JSON.
 * @type {*}
 * 
 * @throws {OpenAjax.widget.Error.Inactive} If widget is not currently active.
 */
<Widget>.OpenAjax.getPropertyValue = function( name ) {};


<Widget>.OpenAjax.setPropertyValue

The setPropertyValue() function requests that a new value be assigned to a particular property. (For more on properties, refer to the Properties chapter.)

/**
 * Request that the value of the requested property be changed.   
 * This function may cause the updated value of the 
 * property to be published. This function returns 
 * immediately.
 * 
 * @param {String} name	 
 * 		This is the name of the property to set
 * @param {*} value
 * 		This is the value to which the property is to be set.
 * 		The value must be serializeable as JSON.
 *
 * @throws {OpenAjax.widget.Error.Inactive} If widget is not currently active.
 * @throws {OpenAjax.widget.Error.BadParameters} Invalid params. e.g., value is not JSON-serializable
 */
<Widget>.OpenAjax.setPropertyValue = function( name, value ) {};

In some run-time scenarios, the setPropertyValue() may execute asynchronously such that the property change request does not happen immediately and such that onChange or onChangePROPNAME</code> callback functions triggered by the property change may happen asynchronously.

Widget developers SHOULD NOT call setPropertyValue() within an onChange or onChangePROPNAME callback function to avoid the possibility of an infinite loop.

If the run-time environment does NOT change property's value in response to an setPropertyValue() request, then the run-time environment MUST NOT fire a change event and therefore MUST NOT invoke either of the onChange or onChangePROPNAME callback functions.


<Widget>.OpenAjax.getPropertyNames

The getPropertyNames() function returns an array that contains the names of all of the widget's properties.

/**
 * Return array containing the names of all of the widget's properties,
 * in arbitrary order.
 * 
 * @returns 
 * 		An array containing the names of all of the widget's properties
 * @type {String[]}
 * 
 * @throws {OpenAjax.widget.Error.Inactive} If widget is not currently active.
 */
<Widget>.OpenAjax.getPropertyNames = function() {};

<Widget>.OpenAjax.getMsg

The getMsg() function returns the localized version of the given string.

/**
 * Returns the localized version of the given string.
 * 
 * @param {String} key
 * 		The lookup key for the localization string. 
 * 		This parameter must be an exact string match
 * 		with the 'name' attribute on a <msg> element 
 * 		within a message bundle file.
 * 
 * @returns 
 * 		The localized version of the string.
 * @type {String}
 * 
 * @throws {OpenAjax.widget.Error.Inactive} If widget is not currently active.
 * @throws {OpenAjax.widget.Error.NotFound} Requested message not found.
 */
<Widget>.OpenAjax.getMsg = function( key ) {};

<Widget>.OpenAjax.rewriteURI

The rewriteURI() function returns a revised URI string which the browser can resolve for accessing the supplied URI. Key use case for this API are resolving converting relative URIs into run-time absolute URIs and for the case where the run-time environment relies on a proxy server for accessing the widget's resources (e.g., CSS and JavaScript files).

The uri parameter may be either a relative URI or an absolute URI. In either case, the returned URI is an absolute URI that allows the browser to access the given resource.

Because some run-time environments use proxy servers, widgets SHOULD call rewriteURI() before attempting to access widget resources so that the widget will operate correctly across a variety of run-time environments.


/**
 * Returns a revised URI string that can be used to access a resource.
 * Takes into account the base URLs for the current run-time environment
 * and proxy server URI rewriting.
 * 
 * @param {String} uri
 * 		URI to be proxied. Can be either an absolute or relative URI.
 * 
 * @returns 
 * 		An absolute URI by which the browser can access the given resource.
 * @type {String}
 * 
 * @throws {OpenAjax.widget.Error.Inactive} If widget is not currently active.
 * @throws {OpenAjax.widget.Error.BadParameters} Invalid params
 */
<Widget>.OpenAjax.rewriteURI= function( uri ) {};


<Widget>.OpenAjax.hub object

When a widget is instantiated, the run-time environment MUST place an OpenAjax.hub sub-object onto the widget instance object.

The OpenAjax.hub sub-object implements the HubClient interfaces defined in the OpenAjax Hub 2.0 Specification, thereby allowing the widget to interact with the run-time environment and other widgets through the OpenAjax Hub's publish and subscribe APIs. For example:

MyWidgetClass = function() {}
MyWidgetClass.onLoad() {
  MySubscribeCallback = function(topic, publisherData, subscriberData) {
    ...
  }
  this.OpenAjax.hub.subscribe("NewData", MySubscriberCallback);
}

In the above example, the widget subscribes to the "NewData" event. When "NewData" events arrive, the MySubscribeCallback function is invoked.


OpenAjax.widget global object

Widget run-times MUST provide the OpenAjax.widget global object. The following subsections define the APIs on the OpenAjax.widget global object.

OpenAjax.widget.byId

The byId() function returns a widget instance given a widget ID string - i.e., the __WID__ substitution variable. (For more information about the __WID__ substitution variable, see Widget ID substitution in the Variable Substitution chapter.)

/**
 * Global function that returns a widget instance whose ID is wid
 * 
 * @param {Object} wid
 * 
 * @returns a widget instance, or null if none can be found
 * @type {Object}
 * 
 * @throws {OpenAjax.widget.Error.Inactive} If widget is not currently active.
 * @throws {OpenAjax.widget.Error.BadParameters} Invalid params. e.g., value is not JSON-serializable
 */
OpenAjax.widget.byId = function(wid) {}


OpenAjax.widget.Error

OpenAjax.widget.Error defines exception names raised by the APIs defined in this chapter.

OpenAjax.widget.Error = {
	// The widget is not active at this time
	Inactive:"OpenAjax.widget.Error.Inactive",
	// Either a required argument is missing or an invalid argument was provided
	BadParameters:"OpenAjax.widget.Error.BadParameters",
	// The requested resource could not be found
	NotFound:"OpenAjax.widget.Error.NotFound"
};



<--previous       contents--^       next-->
Personal tools