Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 23 Next »

This page will give you some example of how to start your first widget and breakdown of how the Infinit.e Widget framework interacts with your widget.

This guide is written to be picked up from the end of the Getting Started tutorial for getting set up with the Eclipse Plugin and Widget Library.

Overview of an Infinit.e Widget

An infinit.e widget is a flex module that gets run inside a window in the infinit.e application.

The Infinit.e Webpage handles all interactions with the window such as moving, dragging, changing size, hiding/showing, and data transfer.  You are responsible for handling the content inside the widget.

A widget must implement the com.ikanow.infinit.e.widget.library.widget.IWidget interface which is included in the infinit.e.widget.library.swc library.  This involves implementing the following functions: onInit(IWidgetContext), onReceiveNewQuery(), onReceiveNewFilter(), and onParentResize(Number,Number).  The first 3 functions are for data transfer while the last is used for resizing the content you create.

If you are using the Infinit.e Eclipse Plugin then these methods will already be stubbed out for you with sample code.

Overview of function to implement

The IWidget interface requires you to implement a number methods, which will be called from the framework.  We will go over each method in detail below.

Note that all these methods are stubbed out to do nothing by the plug-in, therefore only required functionality need be implemented. (All the functions must be present for the module to compile).

Mandatory
  • onInit(context:IWidgetContext) - This function is called when your widget has completed loading into the Infinit.e Framework.  The current data context object is passed to the widget which you will want to store locally.  This IWidgetContext object contains methods to access the current result sets from the query, filter, and other associated information.  This method has been stubbed out if you are using the Infinit.e Eclipse Plugin and no further action needs taken.
  • onReceiveNewQuery() - This function is called by the framework when new data has been loaded into the IWidgetContext object.  This is your notice that the dataset has changed and you can display new content in your widget.  Some sample code has been stubbed out in the Infinit.e Eclipse Plugin that shows how you can get the query result set from the context object (passed to you on the onInit() function) and then that data can be iterated over to access various fields in the document set.  An example is explained below on using query or filter data. TODO
  • onRecieveNewFilter() - This function is similary to the onReceiveNewQuery function except it is called when a filter has been put on the query data set (even if this filter was applied by this widget).  It can be used similarly to the onReceiveNewQuery function and an example can be seen below.
Optional
  • onParentResize(newH:Number,newW:Number) - This function is used to resize data in your widget.  When the parent window the widget is housed in gets resized, it will let the module inside know that it has changed width and height so you can change your data accordingly.  If you are using the Infinit.e Eclipse Plugin this method has stubbed out an example for you that will set this modules width and height to the incoming width and height so the parent and internal module will stay the same size.  Some examples where you may not want to do this is if you want to instead scale the data in your window so it is always shown or resize in another manor.
  • onSaveWidgetOptions():Object - This function is called periodically by the framework. The user can return an arbitrary JSON object (which is equivalent in ActionScript to an anonymous object, eg "var json:Object = { 'test': 'string', 'numbers': [ 1, 2, 3] };") which is stored by the framework and returned when the object is re-opened. This allows developers to save the widget state (note the size and position on the framework canvas is stored by default), such as level of zoom, which graphs are displayed, etc etc.
  • onLoadWidgetOptions(widgetOptions:Object) - This function restores the widget state saved by the above callback.
Advanced
  • supportedExportFormats() - This function lets developers return an array collection of strings that appears in the widget's context menu. If one of these is selected by the user, onGenerateExportData (see below) is called back with the "format" parameter set to whichever string was selected.
  • onGenerateExportData(filename:String, format:String):ByteArray - This function enables widget developers to generate arbitrary output (normally based on the visualization in the widget, eg KML for a map widget, RDFs for a link analysis widget, etc).
  • onGeneratePDF(printPDF:PDF, title:String):PDF - This function is a special case of the above data export, allowing use of the AlivePDF library to create PDFs. If this function is left stubbed out then a bitmap snapshot of the widget will be generated instead. Override the function in order to support searchable text in the PDF, different formats etc.

Functionality provided by the framework context

In addition to the above callbacks, the WidgetContext object provided to the developer by onInit allows a rich set of active operations to be performed, ranging from simple retrieval of documents to complex interactions such as modifying the framework's query builder or even performing local queries.

Future documentation will cover these capabilities in more detail, in the meantime:

  • The API documentation is maintained here.
  • The sections below provide examples on using this API in order to perform common tasks.
  • The source code of the demonstration widgets can be obtained as a source of further examples.

Example of displaying data received from a query or filter

Here we will step through and example of displaying the titles and entities of a query to show how you can display data your widget receives.

1. First we will create 2 lists in our widget, the left list will show the documents titles, and the right list will show all the entities in the resulting dataset.
Lets first create a group to hold our 2 lists so they will align appropriately:

<s:HGroup width="100%" height="100%">
     <s:List id="titleList" width="50%" height="100%" dataProvider="{titleArrayList}" />
     <s:List id="entityList" width="50%" height="100%" dataProvider="{entityArrayList}" />
</s:HGroup>

Insert this text just below the Module tag in your sampleWidget.
Also create the 2 ArrayCollections we are using as dataProviders for the list inside the <fx:Script> block:

import mx.collections.ArrayCollection;
[Bindable] private var titleArrayList:ArrayCollection = new ArrayCollection();
[Bindable] private var entityArrayList:ArrayCollection = new ArrayCollection();

Now we just need to load some data into these ArrayCollections and the lists will populate so we can show some data.  Let's look at the onReceiveNewQuery function.
This function is going to get called everytime a query is ran so we know to display new data.  Let's first clear our ArrayCollections of any old data, insert this code block at the beginning of the onReceiveNewQuery function:

titleArrayList.removeAll();
entityArrayList.removeAll();

Next we will loop through the result set we get from the IWidgetContext object we saved in the onInit function and add the documents titles to our ArrayCollection.  At the same time we will loop through the document entities adding them to our entity ArrayCollection so we can show their names also:  Add this code below the code we just added in the previous step:

var queryResults:ArrayCollection =  _context.getQuery_AllResults().getTopDocuments();
for each (var doc:Object in queryResults )
{
     titleArrayList.addItem(doc.title);
     for each ( var entity:Object in doc.entities )
     {
          entityArrayList.addItem(entity.disambiguous_name);
     }
}

Now we can run our example, login and do a sample query and we should have some data displayed in our lists, all the results titles, and every entity in all the resulting documents!

An alternative that just showed the aggregated entity information might look like:

var queryResults:ArrayCollection =  _context.getQuery_AllResults().getEntities();
for each (var ent:Object in queryResults )
{
     entityArrayList.addItem(entity.disambiguous_name);
}

And so on.

See here for more details about the different views of the data provided by the IWidgetContext class.

You can download the full code example here or see the widget code below.

More advanced operations

Filtering the data visible by widgets

Suppose you want to see quickly only those documents containing a specific set of entities, but don't want to make a whole new query. For example, you have a "Document Browser" widget open (like the first example above), and also an "aggregated event" widget (like the second example above) eg a "Significance" widget, and you want to see all documents in the "document browser" containing the first entity listed.

In the "aggregated event" widget in some callback (eg click on canvas), you would simply write some code like this:

var entitiesToFilter:Set = new HashSet();
entitiesToFilter.add(_context.getQuery_AllResults().getEntities().getItemAt(0));
_context.filterByEntities(FilterDataSetEnum.FILTER_GLOBAL_DATA, entitiesToFilter, EntityMatchTypeEnum.ANY, IncludeEntitiesEnum.INCLUDE_ALL_ENTITIES);
// (Enums mean: (1) filter starting with all data, (2) apply OR to multiple entities in set, and (3) leave all entities in the resulting document set, not just those in the filter set)

After the final "filterByEntities" call, all active widgets have their "onReceiveNewFilter" callback invoked. This can be used analogously to the "onReceiveNewQuery" example shown above:

public function onReceiveNewFilter():void {
   var queryResults:ArrayCollection =  _context.getQuery_FilteredResults().getTopDocuments();
   for each (var doc:Object in queryResults)
   {
        titleArrayList.addItem(doc.title);
        for each ( var entity:Object in doc.entities )
        {
             entityArrayList.addItem(entity.disambiguous_name);
        }
   }
}

Note the filtering applies to all widgets, including the one making the call.

A visual example of widget filtering is available here.

Saving the widget state across sessions

By default, when a widget is closed it loses all of its state (for example, in a "document search results" type widget, you might have a drop-down list specifying the number of documents to show per page). The exception to this is the location and size of the widget in the framework canvas.

The "onSaveWidgetOptions" and "onLoadWidgetOptions" provide an easy-to-use capability to store any desired state across sessions (ie closing and then re-opening a widget).

For example, assume a flex "ComboBox" object defined in the MXML, with id="documentsPerPage" (with options "5", "10", "20" and "50", from a bound array "_documentsPerPage"). Then the following code fragment would save this option for the logged-in user:

public function onSaveWidgetOptions():Object {
	var json:Object = { documentsPerPage: documentsPerPage.selectedItem.label };
	return json;
}
public function onLoadWidgetOptions(json:Object):void {
	if (null != json) {
		var option:String = json.documentsPerPage;
		if (null != option) {
			for (var i:int = 0; i < _documentsPerPage.length; ++i) {
				if (option == _documentsPerPage[i]) {
					documentsPerPage.selectedIndex = i;
					break;
				}
			}
		}
	}
}

Notes:

  • "onSaveWidgetOptions" is called periodically by the framework (so shouldn't block)
  • "onLoadWidgetOptions" is called after the widget's intialization is complete.
  • As can be seen by the code fragments above, the "Object" passed to from the callbacks represents a JSON object.

Adding a query term to the builder

This is a somewhat more advanced use of the IWidgetContext API, and requires some familiarity with the JSON query API.

TODO

Performing a local query

This is also a somewhat more advanced use of the IWidgetContext API requiring some familiarity with the JSON query API.

TODO

Code annex

<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!--

The MIT License
Copyright (c) 2011 IKANOW llc

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

-->
<mx:Module xmlns:fx="http://ns.adobe.com/mxml/2009"
		   xmlns:s="library://ns.adobe.com/flex/spark"
		   xmlns:mx="library://ns.adobe.com/flex/mx" layout="absolute"
		   implements="com.ikanow.infinit.e.widget.library.widget.IWidget" creationComplete="{dispatchEvent(new Event("Done Loading"));}">
	<s:HGroup width="100%" height="100%">
		<s:List id="titleList" width="50%" height="100%" dataProvider="{titleArrayList}" />
		<s:List id="entityList" width="50%" height="100%" dataProvider="{entityArrayList}" />
	</s:HGroup>
    <fx:Style source="../com/ikanow/infinit/e/assets/styles/infiniteDefaultStyle.css" />
    <fx:Style>
		@namespace s "library://ns.adobe.com/flex/spark";
		@namespace mx "library://ns.adobe.com/flex/mx";
		/* If you need to override a style in our stylesheet, or add another
		style that we did not support you can do so here, an example has been commented out
		Please see documentation about over-riding MX component styles to display fonts
		*/
		/*
		mx|Text
		{
			font-family: infiniteNonCFFFont;
		}
		*/
	</fx:Style>
	<fx:Script>
		<![CDATA[
			import com.ikanow.infinit.e.widget.library.data.IWidgetContext;
			import com.ikanow.infinit.e.widget.library.widget.IWidget;

			import mx.collections.ArrayCollection;

			private var _context:IWidgetContext;
			[Bindable] private var titleArrayList:ArrayCollection = new ArrayCollection();
			[Bindable] private var entityArrayList:ArrayCollection = new ArrayCollection();

			/**
			 * IWidget interface to receive data object (IWidgetContext).
			 * Store the iwidgetcontext so we can receieve data later.
			 */
			public function onInit(context:IWidgetContext):void
			{
				_context = context;
			}

			/**
			 * IWidget interface that fires when a new query is done.
			 * We can access the data from the query by using our
			 * iwidgetcontext object _context.getQueryResults().
			 */
			public function onReceiveNewQuery():void
			{
				titleArrayList.removeAll();
				entityArrayList.removeAll();
				var queryResults:ArrayCollection =  _context.getQuery_AllResults().getTopDocuments();
				for each (var doc:Object in queryResults )
				{
					titleArrayList.addItem(doc.title);
					for each ( var entity:Object in doc.entities )
					{
						entityArrayList.addItem(entity.disambiguous_name);
					}
				}
			}

			/**
			 * IWidget interface that fires when a new filter is done (including from ourself)
			 * We can access the data fromt he filter by using our
			 * iwidgetcontext object _context.getFilterResults().
			 */
			public function onReceiveNewFilter():void
			{
				var filterResults:ArrayCollection = _context.getQuery_FilteredResults().getTopDocuments();
                                // Eg duplicate code from onReceiveNewQuery
			}

			/**
			 * function to rescale the module when the parent container is being resized
			 *
			 * @param newHeight The new height the component needs to be set to
			 * @param newWidth The new width the component needs to be set to
			 */
			public function onParentResize(newHeight:Number,newWidth:Number):void
			{
				this.height = newHeight;
				this.width = newWidth;
			}
			/**
			 * Stubbed out/unusued callbacks:
			 */
			public function supportedExportFormats():ArrayCollection
			{
				return null;
			}
			public function onGenerateExportData(filename:String, format:String):ByteArray
			{
				return null;
			}
			public function onGeneratePDF(printPDF:PDF, title:String):PDF
			{
				return null;
			}
			public function onSaveWidgetOptions():Object
			{
				return null;
			}
			public function onLoadWidgetOptions(widgetOptions:Object):void
			{
			}

		]]>
	</fx:Script>
	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>
</mx:Module>
  • No labels