Creating Your Own Chart Types


Business User Edition supports the ability to add new, custom chart types to its list of built-in charts. These custom chart types are called extensions or plug-ins. An extension is a block of code that accesses resources external to Business User Edition. This topic describes the structure of an extension and the steps necessary to create your own and add it to the chart library.

Introducing Chart Extensions

Chart extensions are written in JavaScript. The visual part of a visualization can be drawn with HTML, Canvas, or SVG. Extensions can include external CSS and JS libraries (such as d3), which can be used to build almost any visualization. The WebFOCUS Extension API is limited to new, complete chart types only. It is not possible to add features to existing chart types, and it is not possible to modify or extend parts of Business User Edition outside of the chart area allocated to your extension.

This topic summarizes the process of writing, configuring, and installing a chart extension. Detailed instructions can be found on the Information Builders GitHub site:

Business User Edition extensions must be placed in the extensions folder under the web_resource folder of your Business User Edition installation. By default, this is the following location:




Is your Business User Edition installation directory.

Several sample chart extensions have already been installed in the extensions folder so that you can see their code, their structure, and how they are accessed in Business User Edition.

Note: The user installing the extension must know how to write JavaScript code for what the chart extension needs to generate. The GitHub site documents how to make the extension conform to the WebFOCUS API and how to install the extension in the Business User Edition chart library. It does not describe how to write JavaScript code.

Creating a Chart Extension


This section summarizes the build cycle for creating an extension and the structure and components of an extension.

Reference: Build Cycle for Writing an Extension

Creating an extension often involves cycles of writing, running, and then debugging code.

When you make changes to the properties.js file for your extension, you need to clear the Business User Edition cache in order for those changes to be recognized. Clear the cache using the Clear cache link in the Administration Console.

If you change the .js code for your extension (for example, com.ibi.simple_bar.js), you do not need to make any changes to Business User Edition. You only need to clear your own browser cache, to ensure that the new JavaScript file is downloaded. The same is true if you change any additional .js files included by your extension.

Reference: Extension Structure

The Simple Bar extension example demonstrates the required and optional files in an extension, and how those files are typically laid out.

You can open com.ibi.simple_bar and com.ibi.simple_bar.js in a text editor to see exactly how an extension is written.

The extension ID (ext_id) is a string in the form com.your_company.extension_name. The ext_id must be all lowercase, and can include only letters, numbers, underscores and dots. The entire extension lives in a folder named ext_id. The core of the extension lives in a file named ext_id.js. This file includes code to render the extension as a new chart type within Business User Edition.

The properties.json file configures your extension to run in Business User Edition. This file includes all the metadata needed to include your extension in Business User Edition, as well as a list of all properties you wish to expose to end users, so they can customize the behavior of your extension.

The extension folder can also include optional additional folders for external css and lib resources. If your extension uses any additional CSS or JavaScript library files, you can keep those resources organized in dedicated folders, such as css and lib, as you choose. External resources are configured and loaded inside the base ext_id.js file of your extension.

Using the Chart Extension API


To see examples of everything that the chart extension API provides, look at com.ibi.simple_bar.js. It is divided into two main parts, chart rendering and extension configuration.

Rendering Charts


The extension API provides three entry points that you can use as needed by defining your own JavaScript callback functions. They are passed a set of properties in a config object. Some properties are available during the entire rendering process, and some are only available during render callback.

Reference: Chart Rendering Callback Functions

You can define the following three JavaScript callback functions. Only the renderCallback function is always required.

  • initCallback(successCallback, config) This optional function is invoked by the engine exactly once during library load time, providing a way to implement document.onload initialization code. This function is passed a successCallback, which you must invoke with true if your initialization code succeeded or false if was not successful. If you call successCallback(false), no further interaction with your extension will occur, and your extension will render as an empty page.
  • preRenderCallback(config) This optional function is invoked each time your extension is to be rendered, as the very first step in the overall rendering process. This is a good place to examine and tweak or override any internal chart properties that will affect the subsequent rendering.
  • renderCallback(config) This required function must contain all of the code that will actually draw your chart. The config object will contain the properties described in the following sections.

Each of the three entry point callbacks is passed a config object, which contains a set of useful properties.

Example: Sample renderCallback Function

The following sample renderCallback code renders the Simple Bar extension.

function renderCallback(renderConfig) {
  var chart = renderConfig.moonbeamInstance;
  var props =;
  var container =
   .attr('class', 'com_ibi_chart');
  var data =;
  if (renderConfig.dataBuckets.depth === 1) {
   data = [data];
  var seriesCount = data[0].length;
  var seriesLabels = data[0].map(function(el){return el.labels;});
  data = d3.transpose(data).map(function(el, idx) {
   el = el[0];
   var v = Array.isArray(el.value) ? el.value : [el.value];
   var y0 = 0;
   return, s) {
    return chart.mergeObjects(d, {y0: y0, y1: y0 += d, seriesID: s, value: d, labels: seriesLabels[idx]});
  var w = renderConfig.width;
  var h = renderConfig.height;
  var x = d3.scale.ordinal().domain(pv.range(seriesCount)).rangeRoundBands([0, w], 0.2);
  var ymax = d3.max([].concat.apply([], data), function(d){return d.y1;});
  var y = d3.scale.linear().domain([0, ymax]).range([25, h]);
  var svg = container.selectAll("g")
   .attr('transform', function(d, i){return 'translate(' + x(i) + ', 0)';});
   .data(function(d){return d;})
   .attr("width", x.rangeBand())
   .attr("y", function(d) {return h - y(d.y1);})
   .attr("height", function(d){return y(d.y1) - y(d.y0);})
   .attr('tdgtitle', function(d, s, g) {
//To support tooltips, each chart object that should draw a tooltip
//must set its 'tdgtitle' attribute to the tooltip's content string.
    // Retrieve the chart engine's user-defined tooltip content with getToolTipContent():
    // 's' and 'g' are the series and group IDs for the riser in question.
    // 'd' is this riser's individual datum, and seriesData is the array of data for this riser's series.
    var seriesData =[s];
    var tooltip = renderConfig.modules.tooltip.getToolTipContent(s, g, d, seriesData);
    // getToolTipContent() return values:
    //  - undefined: do not add any content to this riser's tooltip
    //  - the string 'auto': you must define some 'nice' automatic tooltip content for this riser
    //  - anything else: use this directly as the tooltip content
    if (tooltip === 'auto') {
     if (d.hasOwnProperty('color')) {
      return 'Bar Size: ' + d.value + '<br />Bar Color: ' + d.color;
     return 'Bar Size: ' + d.value;
    return tooltip;
   .attr('class', function(d, s, g) {
    // To support data selection and tooltips, each riser must include a class name with the appropriate seriesID and groupID
    // Use chart.buildClassName to create an appropriate class name.
    // 1st argument must be 'riser', 2nd is seriesID, 3rd is groupID, 4th is an optional extra string which can be used to identify the risers in your extension.
    return chart.buildClassName('riser', s, g, 'bar');
   .attr('fill', function(d) {
    // getSeriesAndGroupProperty(seriesID, groupID, property) is a handy function
    // to easily look up any series dependent property.  'property' can be in
    // dot notation (eg: 'marker.border.width').
    return chart.getSeriesAndGroupProperty(d.seriesID, null, 'color');
   .attr('transform', function(d) {return 'translate(' + (x.rangeBand() / 2) + ',' + (h - 5) + ')';})
   .text(function(d, i){return seriesLabels[i];})
  renderConfig.modules.tooltip.updateToolTips();  // Tell the chart engine your chart is ready for tooltips to be added
  renderConfig.modules.dataSelection.activateSelection();  // Tell the chart engine your chart is ready for data selection to be enabled

Reference: Properties That Are Always Available

The following properties are always available.

Property Name



The chart instance currently being rendered.


The data set being rendered.


The block of properties for your extension, as set by the user.


Optional custom data buckets. For information, see Defining and Using Buckets in an Extension.

Reference: Properties Available Only During Render Callback

The following properties are available only during render callback, and are used by your chart rendering code (renderCallback).

Property Name



Width of the container your extension renders into, in pixels.


Height of the container your extension renders into, in pixels.


The ID of the DOM container your extension renders into. Prepend this to all IDs your extension generates, to ensure multiple copies of your extension work on one page.


DOM node for your extension to render into, either an HTML DIV element or an SVG G element, depending on your chosen containerType extension configuration


DOM node containing the specific chart engine instance being rendered.

Configuring Your Chart Extension


Extension configuration consists of two parts.

  • Chart Engine Configuration configures the extension to interact with the chart engine and in Business User Edition Chart mode. This part of the extension configuration is defined in the config object that is passed to the chart renderer functions.
  • Chart Interface Configuration interacts with the chart type picker in the user interface and the chart attribute categories. This part of the extension configuration is defined in the properties.json file.

Creating a config Object for Chart Engine Configuration


To configure your extension, create a config object with all the information unique to your extension, then register your extension with the extension API.

Reference: Creating a config Object for Your Extension

Required and optional properties in your config object are described in the following table.

Property Name



Is the extension ID described in Extension Structure.


Is the name for the chart type to be displayed in the user interface.


Is a description for the chart type to be displayed in the user interface.


Is either 'html' or 'svg' (the default).


Optional. References your initCallback function, described in Rendering Charts.


Optional. References your preRenderCallback function, described in Rendering Charts.


Required. References your renderCallback function, described in Rendering Charts.


Optional. Are additional external resources (CSS and JS) required by this extension.

Example: Sample config Object

The following code is a sample of the config object used with the Simple Bar extension.

var config = {
    id: 'com.ibi.simple_bar',     // string that uniquely identifies this extension
    containerType: 'svg',  // either 'html' or 'svg' (default)
    initCallback: initCallback,  // Refers to your init callback fn (optional)
    preRenderCallback: preRenderCallback,  // Refers to your preRender callback fn (optional)
    renderCallback: renderCallback,  // Refers to your render fn (required)
    resources:  {  // Additional external resources (CSS & JS) required by this extension (optional)
        script: ['lib/d3.min.js'],
        css: ['css/extension.css']
Reference: Registering Your Extension

To register your extension with the WebFOCUS extension API, call:

Reference: Tips for Building Your Extension

The easiest way to build your own extension is to clone the Simple Bar example, then tweak it. Assume the ID of the new extension is

  1. Rename root folder to Rename com.ibi.simple_bar.js to
  2. In, delete the inner content of the three callback functions.
  3. In, change the entries for each property in config to match the requirements of your extension.
  4. Add any external resources you need to lib and css, and load them by setting config.resources in
  5. Implement renderCallback in to draw your extension.

Configuring the Chart Interface

Each extension must include a properties.json file, which defines the information needed by Business User Edition when drawing its user interface.

The properties.json file consists of the following blocks.

  • info. This block defines several general purpose configuration options.
  • properties. This block defines any properties of your extension that the end user may want to change. The user can change these properties in the GRAPH_JS blocks in a Business User Edition chart procedure.
  • propertyAnnotations. This block validates the content of the properties block. Everything in properties must appear in propertyAnnotations. The possible types of any non-object (leaf) property in properties must be notated as one of "str", "bool", or "number".
  • dataBuckets. This block defines the set of chart attribute categories that appear in the Query pane in Business User Edition when creating a chart. Each member in the dataBuckets collection is a bucket.

    There are two types of buckets, built-in and custom. Built-in buckets provide an easy way to reuse the existing Business User Edition data bucket logic. There are currently two built-in buckets, tooltip, and series_break. Use any of these buckets by setting the associated dataBuckets property to true.

  • bucket. Each bucket block defines one custom chart attribute category. Each custom bucket requires the following properties:
    • id. This property corresponds exactly to the dataArrayMap and data properties that will be received by the render function for your chart.
    • type. This property defines the type of data field this bucket accepts, "measure", "dimension", or "both".
    • count. Consists of count.min and count.max, which define the minimum and maximum number of fields this bucket can accept. A minimum of 0 means this bucket is optional.
  • translations. Defines translations in different languages for every label to be drawn in Business User Edition. The translation object has one property for each language the extension supports, keyed by ISO-639 two letter locale strings.
Example: Sample properties.json File

The following properties.json file is from the Simple Bar extension.

    // Define some general extension configuration options
    "info": {
        "version": "1.0",  // version number of your extension.
        "implements_api_version": "1.0",  // version number of the WebFocus API used by your extension.
        "author": "Information Builders",
        "copyright": "Information Builders Inc.",
        "url": "",
        "icons": {
            "medium": "icons/medium.png"  // Reference to an image in the extension, used in the WF chart picker
    // Define any properties of your extension that end user may want to change.  
    "properties": {
        "exampleProperty": 50
    // Define the possible values for each property in 'properties'.
    "propertyAnnotations": {
        "exampleProperty": "number"
    // Define the available data buckets drawn in WF's 'Query' data bucket tree.  
    "dataBuckets":  {
        // Choose whether or not to reuse existing WF data buckets.  All optional.
        "tooltip": false,
        "series_break": true,
        // Define your own custom data buckets.  Optional
        "buckets": [
                "id": "value",
                "type": "measure",
                "count": {"min": 1, "max": 5}
                "id": "labels",
                "type": "dimension",
                "count": {"min": 1, "max": 5}
    // Define the set of labels used in the WF interface for buckets and chart type picker.
    "translations": {
        "en": {
            "name": "My Simple Bar Chart",
            "description": "This chart is just a simple bar chart, nothing to see here.",
            "icon_tooltip": "This extension does ...", 
            "value_name": "Value Bucket", 
            "value_tooltip": "Drop a measure here", 
            "labels_name": "Label Bucket", 
            "labels_tooltip": "Drop a dimension here"
        "fr": {
            "name": "Un Bar Chart tres simple",
            "description": "C'est un Bar Chart vraiment simple",
            "icon_tooltip": "This extension does ...", 
            "value_name": "Value Bucket", 
            "value_tooltip": "Drop a measure here", 
            "labels_name": "Label Bucket", 
            "labels_tooltip": "Drop a dimension here"

Accessing Data for Your Extension


Each time an extension is rendered, the render callback for the extension is passed the current data set using the argument. The overall structure of the data set is defined by the set of buckets listed in the properties.json file, while the specific content of the data is defined by the data fields the user has added to each bucket.

Defining and Using Buckets in an Extension

The data set is passed into an extension using the data property of the first argument of the render callback, typically named renderConfig. Additional information about the current set of fields in each bucket is in renderConfig.dataBuckets.

A data set is represented in JavaScript as arrays of objects. If an extension defines only custom buckets, the data set will be a flat array of objects. If an extension uses some built-in buckets, the data set may contain deeply nested arrays of arrays. The renderConfig.dataBuckets.depth property will be set to the number of array dimensions in the current data set.

Custom Buckets

Each innermost object within the arrays of data (called a datum) will have one property for each data bucket that contains a field. Each property will be the id of a custom bucket, as defined in the dataBuckets.buckets section of properties.json. The type of values of these properties depend on the bucket type. Dimension buckets have string values, while measure buckets have numeric values. If a bucket contains more than one field, the associated property for each innermost object will be an array of string or number values.

If you implement the extension API version 2, you can retrieve the field name and number format associated with a data bucket entry. To use API 2 and get a bucket entry field name or number format, an extension must declare that it implements extension API version 2 using the 'implements_api_version' entry in the info block of properties.json:

	"info": {
		"implements_api_version": "2.0"

Built-in Buckets

An extension can use buckets that are built-in and predefined by Business User Edition. These buckets will affect more than just the data set. Each bucket will also set specific chart engine properties, to pass in additional information related to that bucket.

Each built-in bucket is either a standard bucket or a break bucket.

  • Standard buckets behave exactly like custom buckets. The data set remains a single array, and each datum object will include an additional property named after the bucket.
  • Break buckets divide the data set into additional arrays of data. For each break bucket used, each datum object will be transformed into a full array of datum objects. The number of datum objects in each array will remain unchanged, but the number of arrays or datum arrays will correspond to the number of entries in the break field.

Types of Break Buckets

Break buckets can be of two types:

  • A series-break bucket breaks the data set into one array for each entry in the series break field chosen by the user. A series-break bucket uses series-dependent properties defined in the chart engine, and the data names are now listed in those series-dependent properties. Each entry in the series-break field will generate a corresponding series property object in the chart engine, retrievable with renderConfig.moonbeamInstance.getSeries(x), where x is an integer for the series to be retrieved. getSeries returns an object with properties such as color and label, which are unique to the chosen series.
  • A matrix-break bucket is used for the sort fields that define the columns and rows in a matrix chart. A matrix-break bucket also adds more array dimensions to the data set. A matrix-break bucket is broken into column and row sub-buckets. If either the row or column bucket contains any fields, the data set will contain two additional dimensions of data, even if one of the matrix buckets is empty. That is, the data set will either contain neither row nor column data, or both row and column data, never just one or the other. bucket.depth will always be at least three.

The Tooltip Bucket

The tooltip bucket is not a break bucket, and does not add any additional array dimensions to the data set. Instead, tooltip behaves like a custom bucket. Each inner datum object will contain a property named tooltip, with a value of type string for dimensions, number for measures, and an array of values for multiple fields in the bucket.

The usefulness of this bucket is that in addition to including tooltip-specific data in the data set, Business User Edition also generates meaningful tooltip content for each series. This tooltip content is the same content used for all of the built-in Business User Edition chart types. Using the tooltip bucket means the extension does not have to figure out what ought to go into each tooltip.

Example: Sample Series-Break Bucket Definition

This example uses the following sample data.













Alfa Romeo









The following code defines a series-break bucket for API version 1.0.

        series_break: true,
        buckets: [
            {id: "label", type: "dimension"},
            {id: "value", type: "measure"}

Consider the following fields assigned to each of the buckets:

  • "Country" assigned to the "series_break" bucket.
  • "Car" assigned to the "label" bucket.
  • "Seats" assigned to the "value" bucket.

In the renderConfig function, the object will be similar to the following, in which the Country values are no longer part of the data array. However, a new array starts for each change in the Country value:

 [{labels: "PEUGEOT", value: 5}],
        [{labels: "ALFA ROMEO", value: 4}, {labels: "MASERATI", value: 2],
        [{labels: "TOYOTA", value: 4}],
        [{labels: "AUDI" ,value: 4}, {labels: "BMW", value: 5}]

The renderConfig.dataBuckets object will be defined as follows:

renderConfig.dataBuckets = {
        depth: 2,
        series_break: {title: "Country"},
        buckets: {
            label: {title: "Car"},
            value: {title: "Seats"}

If you are using API version 2.0, dataBuckets.buckets is an array instead of an object. Each entry in this array represents the content of one data bucket. The id property identifies which bucket this is, and the fields array specifies how many entries are in this bucket and the unique information for each (titles, field names, number formats).

The dataBuckets object includes a method named getBucket(). Pass it the name of a bucket and it returns the content of that bucket.

There are two types of buckets, built-in and custom. Built-in buckets provide an easy way to reuse the existing WebFOCUS data bucket logic. There are currently two built-in buckets, tooltip, and series_break. Use any of these buckets by setting the associated dataBuckets property to true.

The dataBuckets block includes the following objects for API Version 2.0.

  • depth. Specifies the number of buckets in the buckets array.
  • buckets. Specifies the properties of all fields for all buckets.
    • id. This property corresponds exactly to the dataArrayMap and data properties that will be received by the render function for your chart.
    • fields. Is an array of fields for each bucket. For each field, defines the field title, name, and number format:
      {"title": "fieldtitle", "fieldName": "fieldname", "numberFormat": "format"}

With API Version 2.0, the content of renderConfig.dataBuckets that is passed to each render callback in an extension provides field title and format information. In the following example, the there are two buckets. The labels bucket has one field, CAR.ORIGIN.COUNTRY, whose title is COUNTRY. The value bucket has two fields, CAR.SALES, title SALES, and CAR.BODY.DEALER_COST, title DEALER_COST.

"dataBuckets": {
   "depth": 2,
 "buckets": [
   "id": "labels",
   "fields": [
   {"title": "COUNTRY", "fieldName": "CAR.ORIGIN.COUNTRY"}
  "id": "value",
  "fields": [
   {"title": "SALES", "fieldName": "CAR.SALES",
    "numberFormat": "#,###.00"},
   {"title": "DEALER_COST", 
    "fieldName": "CAR.BODY.DEALER_COST",
    "numberFormat": "#,###"}

Handling Partial and Null Data in an Extension

In many cases, the end user working with an extension cannot populate all of the extension buckets immediately. An extension must correctly handle these partial data cases, and cannot crash if one or more buckets are empty. It is important to check renderConfig.dataBuckets to see which buckets have been populated, and act accordingly.

In addition, data sets are often incomplete, missing some values for a given combination of dimensions and measures. These missing values may show up in the data set as null entries within an array (instead of datum objects), or they may show up as entirely empty arrays. It is important to detect and handle these missing data cases, and render a visualization appropriate for such missing data.

Most extensions require some minimum number of populated buckets before anything can be rendered. Use the count.min properties of each dataBuckets.bucket entry in properties.json to define these minimum requirements. If the fields in all buckets do not meet the minimum counts, then the renderCallback for the extension will not be called. Instead, the noDataPreRenderCallback for the extension is called. This allows the extension to render in a special no data mode. In this mode, the extension should render in grey scale, using renderCallback.baseColor as the main color. This should be a very simplified, sample rendering of the extension.

Example: Sample noDataPreRenderCallback Function

The following noDataPreRenderCallback function is from the Simple Bar sample extension.

function noDataRenderCallback(renderConfig) {
  var grey = renderConfig.baseColor; = [{value: [3, 3]}, {value: [4, 4]}, {value: [5, 5]}, {value: [6, 6]}, {value: [7, 7]}];
  renderConfig.moonbeamInstance.getSeries(0).color = grey;
  renderConfig.moonbeamInstance.getSeries(1).color = pv.color(grey).lighter(0.18).color;

Installing a Chart Extension


  1. Find the extensions folder for your local Business User Edition installation. This is typically the following folder.



    Is your Business User Edition installation directory.

    Note: The WebFOCUS Extension section of the Information Builders GitHub page maintains a list of publicly available and supported extensions. To install one of those, click the extension you want to install, then right click the zip file for that extension, for example, and choose Save link as...

  2. Unzip the downloaded zip file into the Business User Edition extensions folder. For example, for the zip file, this should create the following folder.

    If you are installing your own extension from your own environment, copy or download it to the Business User Edition extensions folder, using the same naming conventions for the folder and the extension ID as described for the sample extensions.

  3. Edit C:\ibi\install_dir\config\web_resource\extensions\html5chart_extensions.json. Create a new line for the new extension in the form:
    "": {"enabled": true},



    Is the name of the extension.

  4. In the Administration Console, click Clear cache. This will force WebFOCUS to reload all extensions.

Following is a sample html5chart_extensions.json.

        "com.ibi.simple_bar": {enabled: true},
        "com.ibi.liquid_gauge": {enabled: false},
        "com.ibi.sankey": {enabled: true}

Note: The Administration Console provides a user interface for installing chart extensions. For information, see How to Install HTML5 Chart Extensions From the IBI GitHub Page.

Reference: Preserving Custom Chart Types When Reinstalling Business User Edition

If you reinstall the Business User Edition, your extensions folder will be overwritten. Therefore, if you have installed any custom chart extensions, you should preserve them by copying them to another location prior to reinstalling the Business User Edition and copying them back to the extensions folder after reinstalling the Business User Edition.

You will also have to copy the entries for your custom extensions into the new html5chart_extensions.json file installed with the new version of Business User Edition.

Note: The extensions that are delivered as part of Business User Edition will be reinstalled automatically, so you should not preserve those extensions. In that way, if any enhancements have been made to those extensions, you will automatically have access to the enhanced versions when you reinstall Business User Edition.

Using Your Extension in a WebFOCUS Request

If you have installed and configured your extension as described, your extension will be available for use in Business User Edition as a chart type in the Other format category under HTML5 Extension, as shown in the following image.

The attribute categories you defined in the dataBuckets object of your extension are available in the query pane.


  • The actual extension to use is identified in the chartType property of the *GRAPH_JS block in the StyleSheet. For example:
    chartType: "com.ibi.simple_bar",
  • Each custom attribute category name is prepended with a greater-than character (>). For example:
    TYPE=DATA, COLUMN=N1, BUCKET= >labels, $
    TYPE=DATA, COLUMN=N2, BUCKET= >value, $
    TYPE=DATA, COLUMN=N3, BUCKET= >value, $
    TYPE=DATA, COLUMN=N4, BUCKET= >value, $
    TYPE=DATA, COLUMN=N5, BUCKET= >value, $

The following is a sample request using the Simple Bar extension.

chartType: "com.ibi.simple_bar",

Run the chart. The output is shown in the following image.