Press ESC or click outside to exit.

# Carrot Search FoamTree

API reference, version 3.6.1

Carrot Search FoamTree is a modern, in-browser implementation of hierarchical Voronoi Treemaps. With animated transitions and zooming, varied layouts and visual customizations, FoamTree can provide an engaging user experience.

Technically, FoamTree is an HTML5 application, all modern desktop and mobile browsers supporting the canvas specification will be able to display the visualization. FoamTree can be integrated into an HTML page using a simple JavaScript API.

An up-to-date version for the latest stable release of FoamTree is always at: `get.carrotsearch.com/foamtree/api`.

# Quick start

You can get your first FoamTree visualization in 3 simple steps:

1. Include `carrotsearch.foamtree.js` in your page.
2. Define an HTML element in which FoamTree should be embedded. The element must have non-zero dimensions.
3. Initialize FoamTree by creating a new instance of the `CarrotSearchFoamTree` class. In the options object passed to the constructor, provide the the identifier of your HTML element and some data to visualize.

Below is a complete working example illustrating the above steps. Please note you may need to correct the path to the `carrotsearch.foamtree.js` script to match the directory structure of your page.

    <!DOCTYPE html>
    <html>
      <head>
        <title>FoamTree Quick Start</title>
        <meta charset="utf-8" />
      </head>

      <body>
        <div id="visualization" style="width: 800px; height: 600px"></div>

        <script src="../carrotsearch.foamtree.js"></script>
        <script>
          window.addEventListener("load", function() {
            var foamtree = new CarrotSearchFoamTree({
              id: "visualization",
              dataObject: {
                groups: [
                  { label: "Your", weight: 1.0 },
                  { label: "First", weight: 3.0 },
                  { label: "FoamTree", weight: 2.0 },
                  { label: "Visualization", weight: 4.0 }
                ]
              }
            });
          });
        </script>
      </body>
    </html>

    var foamtree = new CarrotSearchFoamTree({
      id: "visualization",
      dataObject: {
        groups: [
          { label: "Your", weight: 1.0 },
          { label: "First", weight: 3.0 },
          { label: "FoamTree", weight: 2.0 },
          { label: "Visualization", weight: 4.0 }
        ]
      }
    });

# Introduction

### Requirements

FoamTree requires a HTML5-compliant browser to run. In particular, it uses the canvas and local storage parts of the specification, as well as ECMAScript 5 JavaScript constructs. Most modern browsers meet the requirements and can run FoamTree.

FoamTree is officially supported on the following browsers:

- Google Chrome: two latest versions
- Mozilla Firefox: two latest versions
- Microsoft Internet Explorer: version 9 and later
- Mac OS Safari: version 5 and later
- iOS Safari: version 4.2 and later

FoamTree will most likely work on other modern browsers (such as Opera), though support for these is not a priority.

### Distribution package

FoamTree distribution package contains the following files and directories:

- `api/`: FoamTree API reference.
- `demos/`: a number of complete demo applications illustrating specific aspects of FoamTree.
- `tests/`: FoamTree's test suite.
- `carrotsearch.foamtree.js`: FoamTree implementation, required at runtime.
- `carrotsearch.foamtree.asserts.js`: validates option values passed to FoamTree, useful for development and debugging, not required at runtime.
- `carrotsearch.foamtree.util.*.js`: utility scripts that handle a number of typical programming tasks around FoamTree, such as showing and hiding a Loading... indicator when new data is loading.

### NPM dependency

Since 3.5.0 FoamTree is available as an NPM dependency for use in modern application build systems. The installation is different for the branded demo and the licensed non-branded variants, see the following sections for details.

#### Demo version

To add the FoamTree demo version to your project, run:

    npm install @carrotsearch/foamtree

or

    yarn add @carrotsearch/foamtree

Once the FoamTree dependency is installed, you can import the `FoamTree` class as follows:

      import { FoamTree } from "@carrotsearch/foamtree";

      const foamtree = new FoamTree({ ... });

If you hold a FoamTree license, see the licensed version section for installation instructions.

#### Licensed version

The NPM version of licensed FoamTree is available from our servers through a special URL. To add the licensed FoamTree dependency to your project:

1. Upload your FoamTree license file at https://secure.carrotsearch.com.

2. Copy the URL of the "npm" link under the FoamTree version you'd like to install.

3. Paste the copied link to your `package.json` as follows:

       {
         "dependencies": {
           "@carrotsearch/foamtree": "https://secure.carrotsearch.com/download/foamtree/3.5.0/npm? "
         }
       }

   The hexadecimal data included in the query string of the URL represents your FoamTree license file and allows our servers to determine which FoamTree versions your license file covers.

   Alternatively, you can download the `*.tgz` file from the copied link, store it together with your code and reference the downloaded `*.tgz` file in your `package.json`.

4. Once the FoamTree dependency is installed, import the `FoamTree` class and plugin classes as follows:

       import { FoamTree } from "@carrotsearch/foamtree";

       const foamtree = new FoamTree({ ... });

### API Overview

The core implementation and API of the visualization is contained in the `carrotsearch.foamtree.js` file. It handles all your code's interactions with the visualization, including embedding, changing visual properties, loading new data and listening to events.

The entry point to the API is an instance of the `CarrotSearchFoamTree` class created during embedding.

### Important concepts

The remaining part of this documentation refers to a number of concepts defined below.

data model
  the data FoamTree can visualize; consists of a hierarchical structure of groups
group
  an individual piece of data FoamTree can visualize; represented by a polygon
rollout
  the animated effect FoamTree uses to show a new data model
pullback
  the animated effect FoamTree uses to hide an existing data model

#### Group states

Each group shown in the visualization can be in a combination of four different states (see the image below):

selected / unselected
  groups selected by the user are drawn with a thick outline
open / closed
  if a group is open, its label and polygon are not painted so that the user can interact with its child groups
exposed / unexposed
  an exposed group gets zoomed in, so that the user can inspect its contents more easily; groups that are not exposed at the moment are zoomed out and dimmed
hovered
  a group over which the user's pointer is hovering gets highlighted

#### Group decorations

Each group is drawn with a number of customizable graphical decorations, shown in the image below:

border width
  the distance between neighboring groups of the same level
inset width
  the distance between a group's border and its immediate child groups' borders
stroke width
  width of the line drawn along the group's border
selection outline width
  width of the line drawn to mark selected groups
border radius
  radius of the group's border

### Interaction

Users can interact with FoamTree in the following ways:

On desktop device | On touch device | Action
--- | --- | ---
left click | tap | select group, again to deselect
Ctrl + left click ⌘ + left click |  | select multiple groups
left double click | double tap | expose group
right double click Shift + left double click | two-finger double tap | unexpose group
left click-and-hold | tap-and-hold | open group
right click-and-hold Shift + left click-and-hold | two-finger tap-and-hold | close group
mouse wheel | pinch | zoom in / out
mouse drag | drag | pan around zoomed visualization
Esc rapid zoom out | three-finger pinch | unexpose and close all groups, reset zoom factor

You can use the interaction hints utility to display a simple interaction guide for the users. If needed, you can change the default interaction mappings.

# Basic usage

### Embedding

Before FoamTree can display anything, it needs to be embedded in your HTML page. To embed FoamTree, your page needs to:

1. Load `carrotsearch.foamtree.js`, which contains FoamTree implementation. You will usually use a dedicated `script` tag for this, but you can also concatenate FoamTree code with some other JavaScript on your page to speed up page loading.
2. Define the HTML element that will contain FoamTree visualization. FoamTree will occupy the full width and height of the element.
3. Inspect the static `supported` property of the visualization class to make sure the browser supports FoamTree.

4. Initialize FoamTree by calling the `CarrotSearchFoamTree` constructor and providing the options object as a parameter. The only required option during initialization is the `id` or the `element` option that need to point to the HTML element you defined earlier. In most cases, you will be providing other options during initialization as well, such as the `dataObject` to display or some visual customizations. See the options reference for a complete list of options.

Heads up!

The element HTML needs to have non-zero dimensions at the time FoamTree initializes. In most cases it is enough to initialize FoamTree once the DOM is ready. In certain cases, however, especially when the page is accessed through the file:// protocol, the element will receive its size a bit later. To avoid race conditions, you can initialize FoamTree in the `onload` event.

    <script>
    window.addEventListener("load", function() {
      // Perform FoamTree embedding here
    });
    </script>

Touch devices

Support for touch-based interaction requires some extra embedding steps. Please see the touch devices section for the details.

The following example shows typical embedding code with a minimal data model. This will also be our baseline for examples where the embedding isn't explicit.

    if (CarrotSearchFoamTree.supported) {
      var foamtree = new CarrotSearchFoamTree({
        id: "visualization",
        dataObject: {
          groups: [
            { id: "1", label: "Group 1", groups: [
              { id: "1.1", label: "Group 1.1" },
              { id: "1.2", label: "Group 1.2" }
            ]},
            { id: "2", label: "Group 2", groups: [
              { id: "2.1", label: "Group 2.1" },
              { id: "2.2", label: "Group 2.2" }
            ]},
            { id: "3", label: "Group 3", groups: [
              { id: "3.1", label: "Group 3.1" },
              { id: "3.2", label: "Group 3.2" }
            ]},
            { id: "4", label: "Group 4", groups: [
              { id: "4.1", label: "Group 4.1" },
              { id: "4.2", label: "Group 4.2" }
            ]},
            { id: "5", label: "Group 5", groups: [
              { id: "5.1", label: "Group 5.1" },
              { id: "5.2", label: "Group 5.2" }
            ]}
          ]
        }
      });
    } else {
      console.log("Visualization not supported.");
    }

The reference to the returned instance (the `foamtree` variable) can be used to change options of the visualization or load new data model. The instance is ready to handle method calls as soon as the constructor invocation returns.

### Changing options

After FoamTree initializes you can still change the values of most options using the `set` method. Most commonly, you will be setting a new value for the `dataObject` option to visualize new data.

The following example changes the `groupSelectionOutlineColor` to paint the outline in red. Run the example and select some groups to see the result.

    foamtree.set("groupSelectionOutlineColor", "red");

Heads up!

When changing certain visual properties using the `set` method, for the changes to take effect, you will need to explicitly instruct FoamTree to `redraw` the visualization.

The following code increases the group border and stroke widths. We deliberately defer the call to call the `redraw` method to show that setting the options alone does not update the visualization image and an explicit redraw is needed.

    window.clearTimeout(timeout);

    var timeout;
    timeout = window.setTimeout(function() {
      foamtree.set({
        groupBorderWidth: 20,
        groupInsetWidth: 40,
        groupStrokeWidth: 10
      });

      // Redraw after a while
      timeout = window.setTimeout(foamtree.redraw, 1000);
    }, 500);

### Data model

A data model can be loaded, replacing the current model, by passing the `dataObject` parameter either during embedding or by calling the `set` method, as in the example below.

    foamtree.set({
      dataObject: {
        groups: [
          { label:"Group 1", groups: [
            { label:"Group 1.1" },
            { label:"Group 1.2" },
            { label:"Group 1.3" }
          ]},
          { label:"Group 2", groups: [
            { label:"Group 2.1" },
            { label:"Group 2.2" }
          ]},
          { label:"Group 3" }
        ]
      }
    });

If the data model is not in the format presented above, it needs to be converted prior to visualization. In this example we will load JSON data, convert it to FoamTree format and visualize. We use jQuery to make Ajax requests, but any other library should work equally well. Note how the model changes after a short pause (fetching model data dynamically, network connection is required).

    foamtree.set({
      dataObject: { groups: [ { label: "loading..." }, { label: "Please wait" } ] },
      fadeDuration: 500
    });

    $.ajax({
      url: "../demos/assets/data/census.json",
      dataType: "json",
      success: function(data) {
        var states = data.groups;

        var races = [
          { label: "Hispanic", prop: "PctHisp" },
          { label: "White",    prop: "PctNonHispWhite" },
          { label: "Black",    prop: "PctBlack" },
          { label: "Indian",   prop: "PctAmInd" },
          { label: "Asian",    prop: "PctAsian" },
          { label: "Hawaiian", prop: "PctNatHawOth" }
        ];

        var popByRace = states.reduce(function (index, state) {
          races.forEach(function (spec) {
            var pop = state[spec.prop] * state.Pop;
            index[spec.prop].total += pop;
            index[spec.prop].min = Math.min(index[spec.prop].min, state[spec.prop]);
            index[spec.prop].max = Math.max(index[spec.prop].max, state[spec.prop]);
          });
          return index;
        }, races.reduce(function (index, spec) {
             index[spec.prop] = { total: 0, min: Number.MAX_VALUE, max: 0 };
             return index;
           }, { }));

        var groups = races.map(function(spec) {
          return {
            label: spec.label,
            weight: popByRace[spec.prop].total,
            groups: states.map(function(state) {
              return {
                label: state.Placename,
                weight: (state[spec.prop] - popByRace[spec.prop].min) /
                          popByRace[spec.prop].max
              };
            })
          };
        });

        foamtree.set({
          dataObject: { groups: groups },
          rolloutDuration: 3000
        });
      }
    });

To clear the visualization, set a `null` `dataObject`. If you set any other options along with the new data object, the new options will apply both during the pullback of the current model and rollout of the new model. The example below clears the current model but also sets the duration of the pullback animation. As a result, the current model will disappear in an animated fashion.

    var timeout = window.setTimeout(function() {
      foamtree.set({
        pullbackDuration: 1000,
        dataObject: null
      });
    }, 1000);

### Selection, opening and exposure

Each group shown by FoamTree can get selected, opened or exposed either as a result of user's interactions or through an API call. You can get and set selection, open and exposure state in a number of ways.

#### Getting selection, opening and exposure

You can get information about the currently selected, open and exposed groups by getting the values of the `select`, `open` and `exposure` options, respectively. To get notified about the state changes right after they are made by the user, register listeners for the corresponding events.

#### Setting selection, opening and exposure

You can change selection, opening and exposure in three different ways:

- By setting the `select`, `open` and `exposure` options.

- By calling the `select`, `open` and `expose` methods. The difference compared to setting options is that the methods return a promise that gets resolved when the animations related to the state change have completed.

- By providing the initial group states in the data object using the `selected`, `open` and `exposed` as in the example below.

      foamtree.set("groupSelectionOutlineColor", "#f00");
      foamtree.set("dataObject", { groups: [
        {
          label: "Initially exposed, open & selected",
          exposed: true, open: true, selected: true, groups: [
            { label: "Initially selected", selected: true },
            { label: "Unselected" }
          ]
        },
        {
          label: "Initially open", open: true, groups: [
            { label: "Initially selected", selected: true },
            { label: "Unselected" }
          ]
        }
      ]});

Heads up!

When group selection, opening or exposure is changed through an API call, the relevant event listeners will not be called. Please also see the limitations when relaxation is visible.

### Events

Event listeners are functions invoked by the visualization code when certain conditions or triggers are met. Some of these callbacks are invoked only for user-related actions (for example `onGroupSelectionChanged`), others may be a result of internal state changes (like `onRolloutComplete`). Please see the event reference for a list of all available events.

You can manage event listeners in two ways:

- By getting or setting the related options, or, most commonly, by providing the options during initial embedding. For each option, you can provide an individual function or an array of functions. However, when you get the option value, always an array of functions will be returned, even if there is only one function registered for a specific event. Setting a new function for a specific event will remove all previously registered functions for that event.

  The example below initializes the visualization and registers for notifications about the changes of group selection and exposure.

      var foamtree = new CarrotSearchFoamTree({
        id: "visualization",
        dataObject: {
          groups: [
            { label: "Try" }, { label: "Selecting" },
            { label: "Or" }, { label: "Exposing" },
            { label: "Some" }, { label: "Group" },
            { label: "And" }, { label: "Watch" },
            { label: "The" }, { label: "Console" }
          ]
        },
        onGroupSelectionChanged: function (info) {
          window.console.log("selected", info);
        },
        onGroupExposureChanged: [
          function(info) {
            window.console.log("exposed 1", info);
          },
          function(info) {
            window.console.log("exposed 2", info);
          }
        ]
      });

- By calling the `on` and `off` methods, to add and remove event listeners, respectively. As opposed to using the `set` method, the `on` and `off` methods preserve the previously registered listeners.

  Both the `on` and `off` method takes two parameters: the type of the event and the event listener to add or remove. The example code below adds group selection and exposure listeners:

      var foamtree = new CarrotSearchFoamTree({
        id: "visualization",
        dataObject: {
          groups: [
            { label: "Try" }, { label: "Selecting" },
            { label: "Or" }, { label: "Exposing" },
            { label: "Some" }, { label: "Group" },
            { label: "And" }, { label: "Watch" },
            { label: "The" }, { label: "Console" },
          ]
        }
      });
      foamtree.on("groupSelectionChanged", function (info) {
        window.console.log("selected", info);
      });
      foamtree.on("groupExposureChanged", function(info) {
        window.console.log("exposed 1", info);
      });
      foamtree.on("groupExposureChanged", function(info) {
        window.console.log("exposed 2", info);
      });

  Heads up!

  To get the type string to use with the `on` / `off` method, take the event option name, remove the leading "on" and lower-case the first letter, for example:

  Option name for the `set` method | Event name for the `on` / `off` methods
  --- | ---
  onGroupSelectionChanged | groupSelectionChanged
  onGroupExposureChanged | groupExposureChanged
  onRolloutComplete | rolloutComplete

For advanced topics related to event listeners, see changing event mapping and externally triggered interaction.

### Resizing

If the visualization container element changes its size, the visualization will be scaled proportionally. To redraw it from scratch taking the new size into consideration, you will need to explicitly call the `resize` method. Because resizing can be costly, the most common pattern is to listen to the window's `resize` event and call FoamTree's `resize` method a while after resizing stops:

    window.addEventListener("resize", (function() {
      var timeout;
      return function() {
        window.clearTimeout(timeout);
        timeout = window.setTimeout(foamtree.resize, 300);
      };
    })());

    window["_resizeHook"] = (function() {
      var timeout;
      return function() {
        window.clearTimeout(timeout);
        timeout = window.setTimeout(foamtree.resize, 300);
      };
    })();
    window.addEventListener("resize", window["_resizeHook"]);

    window.removeEventListener("resize", window["_resizeHook"]);
    delete window["_resizeHook"];

To compare the behavior of visualization with and without the resize listener, resize the browser window once the example is visible.

    // With the resize listener installed.
    foamtree.on("relaxationStep", function() {
      console.log("Layout update");
    });

    // Without the resize listener.
    foamtree.on("relaxationStep", function() {
      console.log("Layout update");
    });

On mobile devices one should resize and redraw the visualization after the orientation of a mobile device changes:

    window.addEventListener("orientationchange", function() {
      foamtree.resize();
    });

### Image data

You can get the current image of the visualization as a Data URI by getting the `imageData` option. You can then display the image for the user or let the user download the image.

Some restrictions apply because of browser security sandboxes. See the documentation of the `imageData` option.

### Printing

FoamTree is a regular printable element of the page. You may want to customize the printing style sheets to hide non-relevant elements. To get a higher-resolution bitmap for printing, you can increase the resolution of the `imageData` by providing the `pixelRatio` parameter:

    window.open(foamtree.get("imageData", {
      format: "image/png",
      pixelRatio: 1.5
    }));

### Debugging

To minimize the size of the code required at runtime, FoamTree does not verify whether input options are valid. During development, however, it is strongly recommended to turn on assertions by including the `carrotsearch.foamtree.asserts.js` file. This will automatically enable assertion checking for options passed to the visualization via the `set` method.

The example below shows invalid values of certain options and how they result in console warnings.

    foamtree.set({
      id: undefined,
      onGroupClick: "fooBar",
      imageData: "newImage.png",
      showZeroWeightGroups: "true",
      groupFillType: "invalid-value",
      unknownOption: 1
    });

### Carrot2 data models

If you are using Carrot2 or Lingo3G clustering engine, you can convert the JSON and XML data formats using the following jQuery snippet.

    // Convert clusters to groups from Carrot2/Lingo3G JSON format.
    function convert(clusters) {
      return clusters.map(function(cluster) {
        return {
          id:     cluster.id,
          label:  cluster.phrases.join(", "),
          weight: cluster.attributes && cluster.attributes["other-topics"] ? 0 : cluster.size,
          groups: cluster.clusters ? convert(cluster.clusters) : []
        }
      });
    };

    // Clear the previous model.
    foamtree.set("dataObject", null);

    // Load Carrot2 JSON clusters.
    $.ajax({
      url: "../demos/assets/data/carrot2/ben-and-jerry.json",
      dataType: "json",
      success: function(data) {
        foamtree.set({
          dataObject: {
            groups: convert(data.clusters)
          }
        });
      }
    });

And a similar conversion from the XML format.

    // Convert clusters to groups from Carrot2/Lingo3G XML format.
    function convert(clusters) {
      return $(clusters).map(function(index, cluster) {
        cluster = $(cluster).detach(); // destroys the model, simplifies processing.
        var subgroups = cluster.children("group").size() > 0 ? convert(cluster.children("group")) : [];
        return {
          id:     cluster.attr("id"),
          label:  cluster.find("group > title > phrase").map(asText).toArray().join(", "),
          weight: cluster.attr("score") ? cluster.attr("score") : 0,
          groups: subgroups
        }
      }).toArray();
    };

    // Convert a node to text.
    function asText(i, e) { return $(e).text(); }

    // Clear the previous model.
    foamtree.set("dataObject", null);

    // Load Carrot2 JSON clusters.

    $.ajax({
      url: "../demos/assets/data/carrot2/ben-and-jerry.xml",
      dataType: "xml",
      success: function(data) {
        foamtree.set({
          dataObject: {
            groups: convert($(data).find("searchresult > group"))
          }
        });
      }
    });

### Touch devices

FoamTree core does not contain support for detecting touch-based events, but delegates this to a specialized library. This kind of decoupling allows improvements and bug fixes related to touch event detection to happen independently of FoamTree releases.

There are two ways to enable touch device support in FoamTree:

- Include the Hammer.js library. If FoamTree detects that Hammer.js is available, it will use it to detect touch events and trigger the appropriate actions. Use Hammer.js version 1.0.6, 1.0.7, 1.0.8 or 1.0.9. FoamTree currently does not support Hammer.js 1.1.x and 2.x lines, but you can use them in the externally triggered interaction mode.
- Externally triggered interaction. If you'd like to use a different touch event detection library or your own code, your code can directly trigger the appropriate events.

# Advanced usage

### Default colors

The default way of assigning colors to groups and labels in a model is the "rainbow" color model. The model assigns colors to the first-level groups from the range of hue values defined by the `rainbowStartColor` and `rainbowEndColor` options. Depending on the value of the `rainbowColorDistribution` options, the colors will be distributed radially or linearly across the whole visualization area.

The example below shows a set of groups covering the full spectrum of hues (angles between 0 and 360).

    foamtree.set({
      rainbowStartColor: "hsla(  0, 100%, 50%, 1)",
      rainbowEndColor:   "hsla(360, 100%, 50%, 1)",
      dataObject: {
        groups: (function() {
          var arr = [];
          for (var i = 0; i < 100; i++) {
            arr.push({label: ""});
          }
          return arr;
        })()
      }
    });

Child groups are recursively assigned with colors that introduce a gradient to the lightness components of their parent's color, as demonstrated in the example below, which shows HSL color values assigned to each group.

    function modelGen(subgroups) {
      var arr = [];
      var i;
      for (i = subgroups.pop(); i > 0; i--) {
        arr.push({label: ""});
      }
      if (subgroups.length > 0) {
        for (i = arr.length; --i >= 0;) {
          arr[i].groups = modelGen(subgroups.slice());
        }
      }
      return arr;
    }

    foamtree.set({
      rainbowStartColor: "hsla(  0, 100%, 50%, 1)",
      rainbowEndColor:   "hsla(360, 100%, 50%, 1)",
      groupColorDecorator: function(opts, params, vars) {
        params.group.label = "" +
                  Math.round(vars.groupColor.h) + ", " +
                  Math.round(vars.groupColor.s) + "%, " +
                  Math.round(vars.groupColor.l) + "%";
      },
        dataObject: modelGen([3, 5, 10, 1])[0]
    });

If the default color model does not suit your needs you can customize group colors to your liking by using the `groupColorDecorator` callback.

### Custom colors

Varying the polygon's color based on some property of the corresponding group can help to convey some more information about the data set. For the sake of example, let us revisit the visualization of USA Today census data introduced in the data model section. The original code used the size (weight) of the groups to show how large is the specific ethnic group in the corresponding state. The example below additionally visualizes the USA Today diversity index using the lightness of the polygon – the lower the diversity index, the darker the color.

    foamtree.set({
      dataObject: { groups: [ { label: "loading..." }, { label: "Please wait" } ] },
      fadeDuration: 500
    });

    $.ajax({
      url: "../demos/assets/data/census.json",
      dataType: "json",
      jsonpCallback: "callback",
      success: function(data) {
        var states = data.groups;

        var races = [
          { label: "Hispanic", prop: "PctHisp" },
          { label: "White",    prop: "PctNonHispWhite" },
          { label: "Black",    prop: "PctBlack" },
          { label: "Indian",   prop: "PctAmInd" },
          { label: "Asian",    prop: "PctAsian" },
          { label: "Hawaiian", prop: "PctNatHawOth" }
        ];

        var popByRace = states.reduce(function (index, state) {
          races.forEach(function (spec) {
            var pop = state[spec.prop] * state.Pop;
            index[spec.prop].total += pop;
            index[spec.prop].min = Math.min(index[spec.prop].min, state[spec.prop]);
            index[spec.prop].max = Math.max(index[spec.prop].max, state[spec.prop]);
          });
          return index;
        }, races.reduce(function (index, spec) {
             index[spec.prop] = { total: 0, min: Number.MAX_VALUE, max: 0 };
             return index;
           }, { }));

        var groups = races.map(function(spec) {
          return {
            label: spec.label,
            weight: popByRace[spec.prop].total,
            groups: states.map(function(state) {
              return {
                label: state.Placename,
                weight: (state[spec.prop] - popByRace[spec.prop].min) /
                          popByRace[spec.prop].max,

                // Pass the diversity index as an extra property
                diversity: state.USATDiversityIndex
              };
            })
          };
        });

        foamtree.set({
          dataObject: { groups: groups },
          rolloutDuration: 3000,
          groupColorDecorator: function (opts, props, vars) {
            var diversity = props.group.diversity;
            if (diversity) {
              // If diversity is available, use it to
              // vary the lightness of the group.
              // We assume the diversity is in the 0..1 range.
              vars.groupColor.l = diversity * 100;
            }
          }
        });
      }
    });

Please see the documentation of the `groupColorDecorator` option for more color customization examples.

### Custom content

If needed, you can draw custom content in each FoamTree polygon. Please see the `groupContentDecorator` option for details and examples. Additionally, the images demo shows how to render SVG images inside FoamTree polygons and the GitHub search demo demonstrates some more advanced custom content layout and rendering.

### Choosing layout type

As of version 3.3.0, FoamTree can produce both the organic-looking polygonal layouts and the traditional rectangular layouts. While the polygon-based layout is the default, the rectangular layouts may sometimes be useful. The documentation for the `layout` option summarizes the available arrangements and highlights the possible use cases.

As of version 3.4.0, you can additionally choose between the hierarchical and flattened presentation mode. Please see the `stacking` option for details and use cases.

### Visualizing large data sets

Since version 3.2.0, FoamTree should be able to visualize hierarchies of 100k+ groups with 100+ levels on recent hardware. As of version 3.3.0, FoamTree can also generate traditional rectangular treemaps which are significantly faster to compute than the default polygon-based layout. The following subsections discuss the performance and visual aspects involved.

#### Visualization layout computation performance

Before FoamTree can draw anything, it needs to compute the layout based on the input data. An important component of the layout algorithm is determining the positions and areas of polygons in such a way that the areas correspond to group's weights as closely as possible. To achieve a decent representation accuracy, FoamTree needs to iteratively update positions of all polygons until the accuracy reaches the required level. We call this process relaxation.

For the curious

By default, FoamTree performs relaxation off-screen and does not show the intermediate positions of the polygons. To observe what relaxation looks like, you can set the `relaxationVisible` option to `true`:

    foamtree.set({
      fadeDuration: 1500,
      dataObject: {
        groups: (function() {
          var arr = [];
          for (var i = 0; i < 80; i++) {
            arr.push({
              label: "",
              weight: Math.pow(Math.random(), 5) +
                      (Math.random() < 0.1 ? Math.random() * 2 : 0)
            });
          }
          return arr;
        })()
      },

      // Show the relaxation
      relaxationVisible: true,

      // Make the relaxation last longer
      relaxationQualityThreshold: 0,
      relaxationMaxDuration: 15000,

      // For faster rendering
      groupFillType: "plain"
    });

To make it possible to browse very large hierarchies, since version 3.2.0 FoamTree defers laying out a group's children until the child groups need to be drawn. With the default value of the `maxGroupLevelsDrawn` option, FoamTree will eagerly compute the layout for the first 4 levels of the hierarchy. Further layouts will be computed on demand as the user opens the top-level groups.

The on-demand computation of layout works very well when the large number of input groups is spread over a balanced tree of a sufficient number of level. If, however, there are individual groups with large numbers of child groups, the layout computation can still take some significant time. You can try some of the following tuning tips to improve the performance in such cases:

- Since 3.2.0 Decrease `maxGroupLevelsDrawn`. With a lower number of layouts computed up-front, the initial rendering as well as group opening time should be much lower.
- Since 3.4.5 Use deferred layout computation. The `maxGroupLevelsAttached` option along with the `attach` method can be used to defer the layout of lower hierarchy levels until the layout of the top level is computed and shown to the user. This can improve the responsiveness of the visualization during the initial data loading. See the Deferred layout demo for a complete code example.
- Disable rollout and pullback animations. As the pullback animations move each group in separation, animating thousands will significantly degrade the performance. If your data contains groups with more than several hundreds direct child groups, consider setting `rolloutDuration` and `pullbackDuration` to `0`, which will apply simple fade-in and fade-out effects.
- Replace expose effect with a simple zoom. Similarly to rollout and pullback animations, the expose effect needs to animate each group individually. If your data contains more than several hundreds of groups, use the following code to change the default double-click action from expose to zoom in:

      foamtree.set({
        dataObject: largeDataSet,

        // Store references to parent groups
        onModelChanging: function addParent(group, parent) {
          group.parent = parent;
          if (group.groups) {
            group.groups.forEach(function(g) {
              addParent(g, group);
            });
          }
        },

        // Expose is expensive for large hierarchies.
        // Therefore, when the user double-clicks a group, zoom in to the group.
        onGroupDoubleClick: function (e) {
          e.preventDefault();
          var group = e.secondary ? e.bottommostOpenGroup : e.topmostClosedGroup;
          var toZoom;
          if (group) {
            this.open({ groups: group, open: !e.secondary });
            toZoom = e.secondary ? group.parent : group;
          } else {
            toZoom = this.get("dataObject");
          }
          this.zoom(toZoom);
        }
      });

- Since 3.2.0 Use rectangular layout by setting `layout` to `ordered` or `squarified`. The rectangular layouts do not need preform relaxation and are therefore much faster to compute.
- Increase `relaxationQualityThreshold`. If you'd like to keep the polygonal layout with large data sets, lowering layout quality may help performance. Try a number of values for `relaxationQualityThreshold`, starting from `0.5` through `1.0` up to `10` or so. This will cause FoamTree to perform fewer relaxation iterations and thus improve the layout calculation time. The down side of this kind of optimization is less appealing layout, with a large number of elongated polygons. In extreme cases, you can also try setting `relaxationQualityThreshold` to `Number.MAX_VALUE`. In this case, relaxation will be completely disabled. This may be the only way to proceed with very large data sets.
- Set `relaxationInitializer` to `ordered` or `squarified`. The treemap-initialized layouts are slightly faster to relax and also lead to more pleasing shapes when the number of relaxation iterations is low or when there is no relaxation at all.
- Show relaxation to the user. In addition to the above tuning, you may want to show the relaxation process to the users, so that they don't see the blank screen while waiting for the visualization to settle. You can do that by setting `relaxationVisible` to `true`. Additionally, you can cap the total relaxation time using the `relaxationMaxDuration` option. The total duration of the relaxation in this mode (assuming there is no cap on duration) will be larger compared to the invisible relaxation mode due to a slightly different relaxation algorithm and time spent on drawing intermediate layouts. However, you will be able to deliver some image to the users more quickly.

The following examples show a 1000-group data set visualized with polygonal and rectangular layouts.

Polygonal layout with the squarified treemap initializer and a minimum amount of relaxation.

    var time;
    foamtree.set({
      fadeDuration: 1500,
      dataObject: {
        groups: randomGroups(1000)
      },

      relaxationInitializer: "squarified",
      relaxationQualityThreshold: 6,

      groupBorderWidth: 2,

      // Measure and report layout time
      onModelChanging: function() {
        time = Date.now();
      },
      onRolloutStart: function() {
        console.log("Polygonal layout took " +
          (Date.now() - time) + " ms.");
      }
    });

Rectangular layout using the squarified treemap algorithm, relaxation is not performed in this case.

    var time;
    foamtree.set({
      fadeDuration: 1500,
      dataObject: {
        groups: randomGroups(1000)
      },

      layout: "squarified",
      // relaxation qualiti does not apply here

      groupBorderWidth: 2,

      // Measure and report layout time
      onModelChanging: function() {
        time = Date.now();
      },
      onRolloutStart: function() {
        console.log("Rectangular layout took " +
          (Date.now() - time) + " ms.");
      }
    });

#### Visualization rendering performance

Another important performance factor is the actual drawing of the visualization image. To achieve highest rendering speeds, FoamTree employs a number of optimizations:

- Since 3.2.0 Drawing only the specified number of group levels. With a very deeply nested hierarchy and the default value of the `parentFillOpacity` option only a small number of top layers would be visible. Taking advantage of this, FoamTree does not draw the lower levels of groups until some of their parents gets open for browsing. You can use the `maxGroupLevelsDrawn` and `maxGroupLabelLevelsDrawn` options to customize how many levels of group polygons and labels should be drawn.

- Incremental updates. When only part of the visualization image needs to be updated, for example on group hover or selection change, FoamTree tries to perform an incremental redraw in which only the affected area is cleared and drawn again. This significantly speeds up redrawing the visualization, especially for large data sets.

  Heads up!

  In certain cases, incremental updates are not possible:

  - when some groups are currently exposed
  - when `groupBorderWidth` is `0`
  - when `groupBorderWidth` is less than `groupStrokeWidth``/ 2 + 0.5`
  - when any group is selected and `groupBorderWidth` is less than `groupSelectionOutlineWidth``/ 2 + 0.5 +``groupSelectionOutlineShadowSize`

  To find out whether the redraw operation was incremental or not, check the first parameter passed to the `onRedraw` event listener, if the parameter is `true`, the last rendering operation was an incremental one.

  The following example sets up the `onRedraw` listener to report on the type of redraws FoamTree is performing. Try interacting with the visualization and watch the console to see when incremental redraws are possible.

      foamtree.set({
        groupBorderRadius: 0,
        dataObject: { groups: [
          {
            label: "Click to unexpose and reset options\
                    [will enable incremental draws]",
            action: "reset"
          },
          {
            label: "Click to expose\
                    [will disable incremental draws]",
            action: "expose"
          },
          {
            label: "Click to set groupBorderWidth to 0\
                    [will disable incremental draws]",
            action: "border-width"
          },
          {
            label: "Click to set groupStrokeWidth to 10\
                    [will disable incremental draws]",
            action: "stroke-width"
          }
        ]},
        onRedraw: function (incremental) {
          console.log((incremental ? "Incremental" : "Full") + " redraw");
        },
        onGroupDoubleClick: function(event) {
          event.preventDefault();
        },
        onGroupClick: function (event) {
          var action = event.group.action;
          if (action) {
            event.preventDefault();
            switch (action) {
            case "reset":
              foamtree.expose([]).then(function() {
                foamtree.set({
                  groupBorderWidth: 4,
                  groupStrokeWidth: 1.5
                });
                foamtree.redraw();
              });
              break;

            case "expose":
              foamtree.expose(event.group);
              break;

            case "border-width":
              foamtree.set("groupBorderWidth", 0);
              foamtree.redraw();
              break;

            case "stroke-width":
              foamtree.set("groupStrokeWidth", 10);
              foamtree.redraw();
              break;
            }
          }
        }
      });

- Wireframe and final rendering quality. When animating the visualization, either during rollout or in response to user actions, FoamTree will switch to the wireframe rendering mode. Depending on the size of the data set, wireframe rendering may disable certain expensive to draw elements, such as group gradients, shadows, stokes or labels. As soon as the animation stops, FoamTree will switch back to the final rendering quality and show the visualization with all the details.

  You can influence the amount of details drawn in both modes by changing the `wireframeDrawMaxDuration`, `finalCompleteDrawMaxDuration` and `finalIncrementalDrawMaxDuration` options. On high-DPI displays, you can also set separate pixel densities for the final and wireframe images using the `pixelRatio` and `wireframePixelRatio` options, respectively.

- Caching of geometry information. If the geometry of the layout has not changed between redraws, which is the case when zooming and panning, FoamTree will be able to e.g. avoid re-fitting of the label's text into the polygon.

While FoamTree tries to automatically adjust the level of detail to match the size of the data set and the device speed, you can apply some of tuning described below to further improve the animation smoothness and rendering time:

- Decrease `maxGroupLevelsDrawn` and `maxGroupLabelLevelsDrawn`.
- Decrease the values of the `wireframeDrawMaxDuration`, `finalCompleteDrawMaxDuration` and `finalIncrementalDrawMaxDuration` options, possibly to zero.
- Set `groupFillType` to `plain`.
- Set `groupStrokeType` to `none`.
- Set `wireframeLabelDrawing` to `never`.

#### Visualizing very deep hierarchies

When visualizing hierarchies with 5 or more levels with default option values it may happen that lower-level groups or branches are not rendered at all because there is not enough space in the parent group's polygons. The following tips will help you to ensure that as many groups as possible are rendered:

- Set `stacking` to `hierarchical` for best performance.
- Decrease `groupMinDiameter` to `0`, so that FoamTree keeps laying out child groups until the floating point computation precision limit is reached.
- Decrease `groupBorderWidth` and `groupInsetWidth` to allow more space for the child groups.
- Set `groupBorderRadius` to `0`, for deeply-nested hierarchies rounded borders may look distorted.

### Lazy loading

With FoamTree 3.2.0 and later it is possible to defer the loading of data for lower levels of the hierarchy until the user opens the corresponding parent group. The lazy loading code example demonstrates this approach.

### Changing default event mappings

If you would like to change the default event to action mappings, you can do so by registering a listener for the appropriate low-level event, such as `onGroupClick`, that would prevent the default behavior and perform the desired one.

The following example restores the FoamTree 2.0.x default double click interaction, which was to open the group on double click or Shift + click and close the group on Ctrl + Shift + click.

    foamtree.set("onGroupDoubleClick", function(event) {
      event.preventDefault();
      this.open(event.group);
    });

    foamtree.set("onGroupClick", function(event) {
      if (event.shiftKey) {
        event.preventDefault();
        this.open({
          groups: event.ctrlKey ? event.bottommostOpenGroup : event.group,
          open: !event.ctrlKey
        });
      }
    });

The following example shows how to prevent the visualization from resetting the view when the Esc key is pressed.

    foamtree.set("onKeyUp", function(event) {
      if (event.keyCode === 27) {
        event.preventDefault();
      }
    });

Finally, the example below exposes the group the user is hovering on rather than simply highlighting it. The example changes the exposure parameters a little to make the interaction smoother.

    foamtree.set({
      onGroupHover: (function() {
        var timeout;
        return function(event) {
          event.preventDefault();
          window.clearTimeout(timeout);
          timeout = window.setTimeout(function() {
            foamtree.expose(event.group);
          }, 500);
        };
      })(),
      exposeDuration: 500,
      groupExposureScale: 1.1,
      groupExposureZoomMargin: 0.4,

      fadeDuration: 1500,
      dataObject: {
        groups: (function() {
          var arr = [];
          for (var i = 0; i < 100; i++) {
            arr.push({
              label: "",
              weight: 0.1 + Math.random() +
                      (Math.random() < 0.2 ? Math.random() * 3 : 0)
            });
          }
          return arr;
        })()
      }
    });

### Externally triggered interaction

You can completely override FoamTree's internal event capture layer and substitute it with your own code if needed. One typical use case is when you need to support a non-standard mobile device for which FoamTree's internal event capture does not work very well.

The general pattern for externally triggered interaction is to set the `interactionHandler` option to `external`, set up your own event capture code (using some of the available libraries, such as Hammer.js or Event.js and trigger the appropriate FoamTree actions using the `trigger` method. Please see the external-interaction.html demo for a complete example.

### SVG export

Use the `drawTo()` method in combination with a SVG-capturing canvas context implementation, such as `svgcanvas`.

# Utilities

FoamTree comes with a number of utility scripts that add some extra functionality to the core library. The utilities come as Apache License 2.0 source code, feel free to modify the code to suit your needs. If you don't need to use the utilities, you can still take a look at the source code for some examples of advanced usage of FoamTree API.

## Loading indicator

Loading of data for the visualization but also computation of the diagram can take a while for larger data sets. You can use the `carrotsearch.foamtree.util.loading.js` utility to show a simple Loading... message to the user. Please see the source code of the loading.html example page for usage information.

## Interaction hints and guide

To help the users to discover all the ways in which they can interact with FoamTree, you can use the `carrotsearch.foamtree.util.hints.js` utility. It will show contextual suggestions on how to browse the data (only on desktop browser) and also a guide to all possible interactions and keyboard bindings. Please see the source code of the hints.html example page for usage information.

## Relaxation progress indicator

If FoamTree is set to show layout relaxation using the `relaxationVisible` option, you can use the `carrotsearch.foamtree.util.relaxationprogress.js` utility to show the progress of the process. Please see the source code of the relaxation-progress.html example page for usage information.

# Data structures

Certain data structures listed below are common to many methods and options of FoamTree API.

## Individual group selector

Resolves to one and only one of the groups provided in the `dataObject`. A number of read-only options, such as `geometry`, require an individual group selector as an argument.

Individual groups can be designated by:

- Group identifier, but only if identifiers are provided in the `dataObject`:

      console.log(foamtree.get("geometry", "1"));

- Group object reference:

      var groups = foamtree.get("dataObject").groups;
      console.log(foamtree.get("geometry", groups[0]));

## Multiple group selector

Resolves to zero or more of the groups provided in the `dataObject`. Many options and methods, such as `selection`, require a multiple group selector as a value.

Multiple groups can be designated by:

- Individual group selector:

      foamtree.set("selection", "1");

- Array of individual group selectors. Both id- and reference-based selectors can be mixed in one array:

      var groups = foamtree.get("dataObject").groups;
      foamtree.set("selection", [ "1", groups[1] ]);

- Object with the `groups` property containing an individual group selector or an array of selectors. Certain methods, such as `select`, use a number of additional properties provided in the object, such as `selected` or `keepPrevious`.

      foamtree.set("selection", { groups: "1" });
      var timeout = window.setTimeout(function() {
        foamtree.set("selection", { groups: "2", keepPrevious: true });
      }, 1500);

- Special object `{ all: true }` that resolves to all groups from the `dataObject`:

      foamtree.set("selection", { all: true });

  Also the special object can contain additional properties interpreted by specific methods:

      foamtree.set("selection", { groups: "1" });
      var timeout = window.setTimeout(function() {
        foamtree.set("selection", { all: true, selected: false });
      }, 1500);

- The null or undefined value that resolve to an empty group array.

      foamtree.set("exposure", { groups: "1" });
      var timeout = window.setTimeout(function() {
        foamtree.set("exposure", null);
      }, 1500);

## Event listener function

You can register listener functions to receive notifications of various FoamTree events. Depending on the type of the listener, the function may be called with one or more parameters. Please see the documentation for the specific event for a list of parameters.

In case of low-level interaction events, such as `onGroupClick`, the listener will be called with one parameter – the event details object. It is possible to prevent the default behaviour associated with that interaction (group selection in case of the `onGroupClick` event). To prevent the default behaviour, return `false` or call the `event.preventDefault()` method in any of the listeners registered for the event.

In the context of all listeners, the `this` variable is a reference to the involved FoamTree instance.

## Event details object

Low-level interaction events, such as `onGroupClick`, will receive an event object as the first parameter. The object contains the following properties:

type
  the type of the event, such as `click` or `dragstart`
group
  a reference to the data object corresponding to the group over which the event was triggered
topmostClosedGroup
  an alias to the `group` property
bottommostOpenGroup
  a reference to the data object corresponding to the closest open parent of the group over which the event was triggered
x, y
  coordinates of the point over which the event was triggered. The coordinates are relative to the visualization container element. Use those coordinates to, for example, show a popup dialog near the mouse pointer.
xAbsolute, yAbsolute
  Since 3.4.4 coordinates of the point over which the event was triggered, relative to the visualization area. These coordinates are in the same space as the one used to draw content in the `groupContentDecorator` and are independent of zoom, pan and expose transformations. You can use these coordinates to make custom content interactive.
secondary
  `true` if the event was triggered by the right mouse button or a two-finger gesture on touch devices.
touches
  the number of touches involved in the gesture. On non-touch devices, always equal to `1`.
scale
  the scale factor associated with the event. Meaningful only for the `onGroupTransform` and `onGroupTransformEnd` events, for other events the scale is `1`.
delta
  the direction in which the mouse wheel was rotated. Positive values mean the upwards rotation, negative values mean downwards rotation. Meaningful only for the `onGroupMouseWheel` event, equal to `0` for other events.
keyCode
  the numeric code of the key that was pressed. Meaningful only for the `onKeyUp` event.
ctrlKey
  `true` if the Ctrl key was pressed during the event
altKey
  `true` if the Alt key was pressed during the event
metaKey
  `true` if the meta key was pressed during the event
shiftKey
  `true` if the Shift key was pressed during the event
preventDefault
  a function that can be called to prevent the default FoamTree action, such as group selection on mouse click, associated with the event.
preventOriginalEventDefault
  a function that can be called to prevent the default browser's action, such as page scrolling on mouse wheel, associated with the original event.
allowOriginalEventDefault
  a function that can be called to allow the default browser's action associated with the original event. The default behaviour of FoamTree is to prevent browser's actions for the mouse wheel and touch events to make it possible to use them for visualization interactions. You can restore the default browser's actions by calling this method as shown in the scrollable content example.

For the description of the event objects of non-low-level events, please see the documentation of the specific event.

## Promise

JavaScript promises are an elegant way of getting access to the result of an action that completes asynchronously at some point in future. A very simple example of such a result is the animation completing a certain time after it was initiated.

FoamTree methods that complete asynchronously, such as `expose`, return a promise: a JavaScript object with the `then` method. Using that method you can register one or more callbacks that will be called when the future action completes.

For examples of promises in use, please see the descriptions of the `open` and `expose` methods.

# Methods

### get(*)

get()

  Returns an object containing current values of all options. Properties of the returned object correspond to option names, values are option values.

      console.log(foamtree.get());

get(option)

  Returns the current value of the requested `option`. If the provided string does not correspond to any option name, the result is undefined.

      console.log(foamtree.get("dataObject"));

get(option, args...)

  Returns the current value of the requested read-only `option`, parameterized by the provided `args`. The example below retrieves information about the `geometry` of the group with id `1`. The second argument causes FoamTree to return full geometry data, including coordinates of the polygon's vertices.

      console.log(foamtree.get("geometry", "1", true));

### set(*)

set(option, value)

  Sets the provided `option` to the desired `value`. If the provided `option` string does not correspond to any option, the call is ignored.

      foamtree.set("exposure", "1");

set(options)

  Sets new values for all options included in the provided `options` object. Properties of the object should correspond to attribute names, values of the object will be treated as values to set. Any properties of the options object that do not correspond to any attributes of the visualization will be ignored.

      foamtree.set({
        exposure: "1",
        selection: "1",
        groupSelectionOutlineColor: "red"
      });

Heads up!

For the changes of visual options to take effect, you will need to explicitly call the `redraw` method:

    foamtree.set({
      rainbowStartColor: "#f00",
      rainbowEndColor: "#aa0"
    });
    foamtree.redraw();

### on(type, listener)

Registers a listener for a FoamTree event. As opposed to using the `set` method, the `on` method preserves the previously registered listeners.

The `on` method requires two parameters:

type

  A string that specifies which event to listen to. To get type string to use with this method, take the event option name, remove the leading "on" and lower-case the first letter. For example, the type string corresponding to the `onGroupSelectionChanged` event is `groupSelectionChanged`.

listener

  The event listener function to invoke when the event is triggered.

The following examples registers to receive selection events and exposes the groups that have just been selected. Additionally, the code registers another listener that prevents the default interaction (expose) on double click. Notice that the `this` variable is a reference to the FoamTree instance inside the callback function and can be used to invoke other methods.

    foamtree.on("groupSelectionChanged", function (event) {
      // "this" is the reference to FoamTree instance here.
      // Expose the groups that have just been selected.
      this.expose(event.groups);
    });

    // Prevent the default action (expose) on double click
    foamtree.on("groupDoubleClick", function (event) {
      event.preventDefault();
    });

Please see the Visualization events section for more code examples and the Events section for a list of all available events.

### off(type, listener)

Removes the requested `listener` from the list of listeners of the specified `type`. As opposed to using the `set` method, this method will preserve the other listeners registered for the event.

The `off` method requires two parameters:

type

  A string that specifies which event to remove the listener from. To get type string to use with this method, take the event option name, remove the leading "on" and lower-case the first letter. For example, the type string corresponding to the `onGroupSelectionChanged` event is `groupSelectionChanged`.

listener

  The event listener function to remove.

The following example defines the `once` function that notifies the listener only about the first occurrence of the event. After the listener gets called, the function removes itself from the list of listeners using the `off` method, so that the listener is not called when further events of this type get triggered. Notice how the selection change listener registered using the `once` function gets called only for the first selection change.

    var once = function (type, listener) {
      foamtree.on(type, function onceListener() {
        this.off(type, onceListener);
        return listener.apply(this, arguments);
      });
    };

    once("groupSelectionChanged", function (event) {
      window.console.log("Selection changed for the first time. Further selection changes will not call this function.")
    });

### redraw(animated, groups)

Triggers a complete redraw of the visualization. If one or more visual options of the visualization have been changed using the `set` method, the changes will not be visible until the visualization is redrawn, either explicitly using this method or in response to user actions.

This method accepts the following parameters:

animated
  (optional) if `true`, FoamTree will assume that this redraw is part of an animation and may be followed by further redraws. In such cases, depending on the size of the data, FoamTree may switch to the wireframe rendering mode. The default value of this parameter is `false`.
groups
  Since 3.1.0 (optional) a suggestion which groups need redrawing. If defined, instead of redrawing all groups, FoamTree may redraw only the groups indicated by the provided individual or multiple groups selector. Please note that in certain cases, such as when the layout of the groups changes, FoamTree may still redraw all groups and not just the ones provided in this parameters. Default value of this parameter is `undefined`, which means FoamTree will always redraw all groups.

The cost of the redraw operation depends on the size of the model and the specific values of certain options. Please see the Visualization rendering performance section for more details.

### resize()

If the size of the HTML container element has changed, resizes and redraws the visualization to accommodate to the new size. See the Resizing section for code snippets related to this function.

If the `relaxationVisible` option is set to `true`, calling this method will trigger a complete relaxation of the layout to accommodate it to the new container dimensions. As with the initial relaxation, the resize-triggered relaxation follows the settings provided by the `relaxationMaxDuration` and `relaxationQualityThreshold` options.

The following example makes the relaxation visible, try resizing the browser window to see how relaxation is started again after the container dimensions change.

    foamtree.set({
      fadeDuration: 1500,
      dataObject: {
        groups: (function() {
          var arr = [];
          for (var i = 0; i < 100; i++) {
            arr.push({
              label: "",
              weight: 0.1 + Math.random() +
                      (Math.random() < 0.2 ? Math.random() * 3 : 0)
            });
          }
          return arr;
        })()
      },

      relaxationInitializer: "random",
      relaxationVisible: true,
      relaxationQualityThreshold: 0,
      relaxationMaxDuration: 8000
    });

### update(groups)

Updates the visualization to reflect changes in group weights. If you change the values of the `weight` property of some group, you need to call this method to see the updated visualization.

This method accepts one optional parameter:

groups
  (optional) if an individual or multiple group selector is provided, only the weights of the groups contained in the specified groups will be updated.

While updating the visualization, FoamTree will respect the relaxation options. For example, to see the process of visualization update, you can set `relaxationVisible` to `true`.

Note: currently, only updating weights of existing groups is supported. Hierarchy changes, such as adding or removing groups, cannot be performed using this method. The only way to visualize an updated hierarchy is to set a new value for the `dataObject` option.

The following example increases a group's weight on left-click and decreases the weight on right-click. The example sets `relaxationVisible` to `true` to animate the weight transitions.

    foamtree.set({
      fadeDuration: 1500,
      dataObject: largeDataSet,

      // Make relaxation and group size changes animated
      relaxationVisible: true,
      relaxationQualityThreshold: 0.5,
      relaxationMaxDuration: 8000,
      // Modify group's weight on click
      onGroupClick: function (e) {
        e.preventDefault();

        // On left-click, increase the weight,
        // decrease on right click.
        e.group.weight *= (e.secondary ? 0.5 : 1.5);

        // Update the visualization
        foamtree.update();
      }
    });

The following example updates weights of the child groups of the left-clicked group. Note that only the layout of the child group changes, the top-level polygons remain unchanged.

    foamtree.set({
      fadeDuration: 1500,
      dataObject: largeDataSet,

      // Make relaxation and group size changes animated
      relaxationVisible: true,
      relaxationQualityThreshold: 0.5,
      relaxationMaxDuration: 8000,
      // Modify group's weight on click
      onGroupClick: function (e) {
        e.preventDefault();

        // Change the weights of child groups
        if (e.group.groups) {
          e.group.groups.forEach(function (g) {
            g.weight *= Math.random() + 0.5;
          });
        }

        // Update weights only inside the affected group
        this.update(e.group);
      }
    });

### select(groups)

Changes the group selection. The `groups` parameter must be a multiple group selector designating the groups to select or deselect:

- If the selector is an individual selector or an array of individual selectors, the designated groups will be selected, the selection of other groups will remain unaffected.

      foamtree.select("1");
      var timeout = window.setTimeout(function() {
        foamtree.select("2");
      }, 1500);

- If the selector is an object, two additional properties are supported, apart from the common `groups` and `all` properties:

  selected
    If `true` or not provided, the designated groups will get selected. If `false`, the designated groups will be deselected.
  keepPrevious
    If `true` or not provided, only the selection of the designated groups will be altered. If `false`, the selection of the designated groups will be altered, the other groups will get unselected.

  There are two ways to clear the selection:

      foamtree.select(["1", "2"]);
      var timeout = window.setTimeout(function() {
        foamtree.select({ groups: [], keepPrevious: false });
      }, 2000);

  or:

      foamtree.select(["1", "2"]);
      var timeout = window.setTimeout(function() {
        foamtree.select({ all: true, selected: false });
      }, 2000);

Note: when selection is changed using this method, the event listeners will not be called.

### expose(groups)

Changes the set of exposed groups. The `groups` parameter must be a multiple group selector designating the groups to expose or unexpose:

- If the selector is an individual selector or an array of individual selectors, the designated groups will be exposed, all other groups will get unexposed.

      foamtree.expose("1");
      var timeout = window.setTimeout(function() {
        foamtree.expose("2");
      }, 2000);

  The following example shows how to unexpose all groups.

      foamtree.expose(["1", "2"]);
      var timeout = window.setTimeout(function() {
        foamtree.expose([]);
      }, 2000);

  A similar effect could be achieved using the `reset` method.

- If the selector is an object, two additional properties are supported, apart from the common `groups` and `all` properties:

  exposed
    If `true` or not provided, the designated groups will get exposed. If `false`, the designated groups will be unexposed.
  keepPrevious
    If `true`, only the exposure of the designated groups will be altered. If `false` or not provided, the exposure of the designated groups will be altered, the other groups will get unexposed.

  The following example uses the `keepExpose` property of the group selector to expose one more group in addition to the already exposed ones. Notice that if the parent group of the group to be exposed is closed (groups 3 and 4 in the example), the parents will be automatically opened as part of the exposure process.

      foamtree.expose(["1", "2"]);
      var timeout = window.setTimeout(function() {
        foamtree.expose({ groups: ["3.1", "4.1"], keepPrevious: true });
      }, 2000);

The expose method returns a promise that gets resolved when the exposure animation completes. The following example exposes one of the groups by calling the `expose` method and registers two callbacks to be notified when the animation completes. To make the result easier to observe, we increase the duration of the expose animation a bit.

    foamtree.set("exposeDuration", 2000);
    var timeout = window.setTimeout(function() {
      foamtree.expose(foamtree.get("dataObject").groups[0])
        .then(function () {
          window.console.log("Expose animation complete");
        })
        .then(function () {
          window.console.log("Another callback");
        });
    }, 1000);

Note: when exposure is changed using this method, the event listeners will not be called.

Note: there are some limitations when relaxation is visible.

### open(groups)

Changes the set of open groups. The `groups` parameter must be a multiple group selector designating the groups to open or close:

- If the selector is an individual selector or an array of individual selectors, the designated groups will get open, the state of the other groups will remain unchanged.

      foamtree.open("1");
      var timeout = window.setTimeout(function() {
        foamtree.open("2");
      }, 2000);

- If the selector is an object, two additional properties are supported, apart from the common `groups` and `all` properties:

  open
    If `true` or not provided, the designated groups will get open. If `false`, the designated groups will be closed.
  keepPrevious
    If `true` or not provided, only the state of the designated groups will be altered. If `false`, the state of the designated groups will be altered, the other groups will get closed.

  To close all groups, you can use the selector with the `all` property:

      foamtree.open(["1", "2"]);
      var timeout = window.setTimeout(function() {
        foamtree.open({ all: true, open: false });
      }, 2000);

The `open` method returns a promise that gets resolved when the state transition animation completes. The following example opens one of the groups by calling the `open` method and registers two callbacks to be notified when the animation completes. To make the result easier to observe, we increase the duration of the opening animation a bit.

    foamtree.set("openCloseDuration", 2000);
    var timeout = window.setTimeout(function() {
      foamtree.open(foamtree.get("dataObject").groups[0])
        .then(function () {
          window.console.log("Open animation complete");
        })
        .then(function () {
          window.console.log("Another callback");
        });
    }, 1000);

Note: when open state is changed using this method, the event listeners will not be called.

### zoom(group)

Zooms the visualization to show the specified group. The `group` parameter must be an individual group selector designating the group to zoom to. You can also zoom to the `dataObject` group, in which case the whole visualization area will be shown.

The speed and easing of the zoom transition are controlled by the `zoomMouseWheelDuration` and `zoomMouseWheelEasing` options. The `zoom` method returns a promise that gets resolved when the zooming animation completes.

The following example replaces the default action performed on double click (expose) with plain zooming into the group.

    foamtree.set({
      dataObject: { groups: randomGroups(50) },
      relaxationVisible: true,
      fadeDuration: 1000,
      onGroupDoubleClick: function(event) {
        event.preventDefault();
        this.zoom(event.secondary ? this.get("dataObject") : event.group);
      }
    });

### reset()

Resets the visualization view back to the initial state, which includes: unexposing and closing all groups, resetting the zoom factor to make the whole visualization area visible. The selection state is not affected by this method.

    foamtree.select([ "1", "2" ]);
    foamtree.expose([ "3", "4" ]);
    foamtree.open([ "2", "3" ]);
    var timeout = window.setTimeout(function() {
      foamtree.reset();
    }, 3000);

### trigger(type, event)

Triggers the FoamTree action associated with the event of the provided type. The most common use case for this method is replacing FoamTree's built-in touch event detection infrastructure with the code of your own.

The `trigger` method requires two parameters:

type

  string identifier of the event to trigger. Below is a list of supported events along with the FoamTree actions they trigger:

  click
    a single mouse click event, selects or deselects a group
  doubleclick
    a double mouse click event, exposes or unexposes a group
  hold
    click-and-hold event, opens or closes a group
  mousedown
    stops zoomed visualization panning
  mousewheel
    turning of the mouse wheel up or down, zooms in or out the visualization
  hover
    hovering with the mouse pointer over the group, highlights the group
  dragstart
    starts panning the zoomed visualization
  drag
    pans the zoomed visualization
  dragend
    depending on the speed of panning: continues panning of the visualization or stops panning and corrects the view port in such a way that the edge of the visualization touches the edge of the viewport.
  transformstart
    starts the touch-based zooming of the visualization
  transform
    touch-based zooming of the visualization
  transformend
    completes the touch-based zooming of the visualization, corrects the view port in such a way that the edge of the visualization touches the edge of the viewport. If the last transformation scale was lower than 0.8 and the zooming was performed with three or more fingers, triggers `reset` of the visualization view.
event
  the details of the event to trigger. The object must contain all the properties of the low-level event, apart from the `preventDefault()` method.

Heads up!

Initiating events using the `trigger` method will notify all the involved event listeners. For example, triggering the `click` event will invoke the `onGroupClick` listeners and, if the default action is not prevented by that listener, the `onGroupSelectionChanging` and the `onGroupSelectionChanged` events.

For this reason, calling the `trigger` method in event listeners may lead to the visualization falling in an endless loop.

### attach(groups, maxLevels)

Initializes internal data structures for the provided groups. The `groups` parameter must be a multiple group selector. The `maxLevels` parameter indicates how many group levels to attach, assuming that the group being attached is on level 0.

You can combine this method with the `maxGroupLevelsAttached` option to improve the initial loading responsiveness when visualizing deeply-nested data sets with large number of groups on the top level. The general approach in this case is the following:

1. In the `onModelChanging` event handler, set `maxGroupLevelsAttached` to `1`. This will cause FoamTree to initialize only the top-level groups when new data is loaded.

2. In the `onRolloutComplete` event handler, bring back `maxGroupLevelsAttached` to the default or desired value. Then, call the `attach()` method for each top level group. Make sure to allow the main browser's thread to progress by, for example, making one `attach()` call per one `requestAnimationFrame()` callback.

The `attach()` method returns the number of child groups for which layout has been computed.

A complete example of the above technique is available in the Deferred layout of child groups demo.

### drawTo(ctx)

Draws the entire visualization to the canvas context you provide.

The main use case for this method is exporting the visualization to the SVG format. To do this, provide a canvas context implementation, such as `svgcanvas`, which converts all canvas draw calls to the corresponding SVG shapes.

The following code performs the SVG export based on the `svgcanvas` library.

    // Get the bounding box of the top-level group. This will be the size of the exported SVG.
    const geometry = foamtree.get("geometry", foamtree.get("dataObject"));

    // Create a context that will capture the drawing calls.
    const ctx = new Context(geometry.boxWidth, geometry.boxHeight);

    // Optional: for the duration of the export, set the groupMinDiameter and
    // groupLabelMinFontSize options to 0 to ensure that all labels and groups are visible.
    const groupLabelMinFontSize = foamtree.get("groupLabelMinFontSize");
    const groupMinDiameter = foamtree.get("groupMinDiameter");
    foamtree.set("groupLabelMinFontSize", 0);
    foamtree.set("groupMinDiameter", 0);

    // Draw the visualization to the context.
    foamtree.drawTo(ctx);

    // Restore the original groupMinDiameter and groupLabelMinFontSize options.
    foamtree.set("groupLabelMinFontSize", groupLabelMinFontSize);
    foamtree.set("groupMinDiameter", groupMinDiameter);

    // Get the serialized SVG representation of the visualization.
    const serializedSvg = ctx.getSerializedSvg();

#### Limitations and caveats

- Gradient fills on zoomed viewports. The `svgcanvas` implementation materializes the hierarchy of context transformations directly into the coordinates of the SVG shapes it creates. This causes the incorrect rendering of gradients if the viewport is zoomed and/ or panned.

- Custom group label fonts. If your visualization uses custom web fonts to draw group labels, to preserve the same font in the exported standalone SVG, you must post-process the SVG output to embed the custom font inside the SVG.

### dispose()

Stops all running animations and removes all HTML event listeners registered by FoamTree. If you are repeatedly instantiating and disposing of FoamTree instances on the same page, make sure you call the `dispose` method when appropriate. Otherwise, dangling event listeners will prevent large chunks of memory from being garbage-collected.

# Options

## Embedding

### supported

A static property on `CarrotSearchFoamTree` equal to `true` if visualization is supported on the current browser environment.

Heads up!

This property is static and must be accessed directly on `CarrotSearchFoamTree` class, as shown in this example:

    if (CarrotSearchFoamTree.supported) {
      // Put embedding code here.
      console.log("Visualization supported.");
    } else {
      // Display a warning or skip visualization.
      console.log("Visualization not supported.");
    }

### id

Identifier of the DOM element into which the visualization is to be embedded.

Heads up!

The DOM element must have a non-zero width and height before embedding. The visualization will allocate a canvas of exactly the same size (client size) and will stretch proportionally when the element's size changes. The visualization must be resized manually (see `resize` method) to update to new element's dimensions.

### element

The DOM element into which the visualization is to be embedded. Please see the `id` option for additional considerations.

## Data

### dataObject

The data to visualize. The provided value must be an object or `null` / `undefined`. The object will be assumed to represent the root group and must contain a non-empty `groups` property pointing to an array of objects representing individual first-level groups. A `null` or `undefined` object clears the visualization.

Each group can contain the following properties:

id
  (optional, String) unique identifier of the group. Group identifiers are required only for programmatic changes of certain group attributes such as `selection`, `exposure` or `open` state.
label

  (required, String) textual description of the group. For best results, use short labels.

  FoamTree will handle the following special unicode characters in the label text:

  Unbreakable space (\u00a0)
    line breaks will not be made on the unbreakable space character
  Soft hyphen (\u00ad)

    when label needs to be broken into multiple lines, soft hyphen will allow a line break, generating the hyphen character at the end of the line. A soft hyphen will not add any spacing between the characters that surround it.

    If you'd like to have your labels hyphenated, you can use an external hyphenation utility, such as Hyphenator.js to insert soft hyphens into your labels.

  Zero-width space (\u200b)
    when label needs to be broken into multiple lines, zero-width space will allow a line break. A zero-width space will not add any spacing between the characters that surround it.
  New line (\n)
    Since 3.4.0 forces a line break
weight

  (optional, Number >= 0) weight of the group relative to other groups. The larger the weight, the more space the group's polygon will occupy on the screen. Good values for the weight property could be e.g. the number of documents in a cluster or the score of the cluster.

  Group weights must be non-negative. Zero-weight groups can receive special treatment, see the `showZeroWeightGroups` option. If a group's weight is not specified, FoamTree will assume the weight is `1.0`.

groups
  (optional, Array) an array of subgroups of the group.
open
  (optional, boolean) if `true`, the group will get open right after the new data is set.
exposed
  (optional, boolean) if `true`, the group will get exposed right after the new data is set. This can be useful to visually highlight a certain group (or groups) as the data is loaded. Please note the limitations when relaxation is visible.
selected
  (optional, boolean) if `true`, the group will get selected right after the new data is set.
description
  Since 3.4.9 (optional, boolean) If `true`, `descriptionGroup` option is set to `always` and `stacking` is `hierarchical`, allocates extra space inside this group to show the group's label together with the group's child groups.
initialPosition
  Since 3.4.10 (optional, object) Determines the initial position of this group. The object must contain two properties: `position` and `distanceFromCenter`. Please see the `attributionPosition` and `attributionDistanceFromCenter` options for semantics of the properties and the Initial positions demo for a complete code example.

The data object (and groups) can contain custom properties, not required or interpreted by the visualization. These properties may be used for color model tuning, for example. The following example shows a data object with various values of the above properties.

    foamtree.set({
      dataObject: {
        groups: [
          { label: "Bad news", open: true, groups: [
            { label: "Last penny lost", sentiment: -0.5 },
            { label: "Bazinga doomed",  sentiment: -1 }
            ]},
          { label: "Good news", exposed: true, selected: true, groups: [
            { label: "iPads under $100",      sentiment: 0.5, selected: true },
            { label: "Martians are friendly", sentiment: 1 }
            ]},
          { label: "Other news", groups: [
            { label: "Vampires on the loose",     sentiment: -2 },
            { label: "Van Helsing to the rescue", sentiment: -3 }
            ]}
        ]
      }
    });

See the Data model and Carrot2 models sections for examples of how to convert data that is not in FoamTree format.

### hierarchy

Returns hierarchy-related information about a group. The returned object contains the following properties:

group
  a reference to the data object representing the related group
parent
  a reference to the data object representing the immediate parent of the group or `null`
level
  the nesting level of the group. Level numbers start at 0.
index
  the position of the group relative to its siblings in the input data object. Position indices start at 0.
siblingCount
  the number of immediate siblings of the group
hasChildren
  `true` if the group has any child groups
weightNormalized
  the weight of the group normalized to the 0..1 range in relation the group's siblings. The raw weight of the group can be retrieved from the original data model if it was present there.
indexByWeight
  Since 3.0.2 the position of the group relative to its siblings in the decreasing weight order, 0 index corresponds to the highest-weight group.
description
  Since 3.4.0 `true` if the group represents the extra polygon holding group label in `flattened` `stacking` mode.
attribution
  Since 3.3.1 `true` if the group is the attribution group.

To retrieve the information about a group, provide the individual group selector as the second parameter of the `get` method:

    console.log(foamtree.get("hierarchy", "1"));

## Layout

Options described in this section determine the arrangement of polygons generated by FoamTree.

### layout

Determines the general type of layout to generate. Depending on this option, FoamTree will produce polygon-based organic-looking visualization or the traditional rectangular tree map.

The following values are supported:

relaxed
  produces polygonal organic-looking arrangements based on Voronoi diagrams. This type of layout tends to visualize groups as near-round polygons leaving plenty of space for the group's label and content. A potential downside of this layout is that it may not very well preserve the original order of groups provided in the data object. See the `relaxationInitializer` option for various possible representations of the group order in the relaxed layout.
ordered
  produces the traditional rectangular treemap using the strip algorithm. The advantage of this layout is that, if `layoutByWeightOrder` is `false`, it will arrange groups in the left-to-right top-to-bottom fashion according to the order they were provided in the data object. One disadvantage of this algorithm is that certain groups may be visualized as high-aspect-ratio rectangles, which will leave little useful space for the label and contents of the group. You can use the `rectangleAspectRatioPreference` option to tune the aspect ratios of the rectangles to some extent.
squarified
  produces the traditional treemap using the squarified algorithm. This layout arranges groups along the diagonal of the container. Aspect ratios of the rectangles produced by this method tend to be lower than the ones from the `ordered` layout. The squarified algorithm, however, does not lay out groups in the left-to-right fashion.

The following table summarizes the possible use cases for the specific layouts.

Requirement | Layout
--- | ---
Organic-looking layout is required. | Use the `relaxed` layout, customize the the `relaxationInitializer` to choose the arrangement of polygons.
Traditional rectangular treemap is required. | Use the `ordered` or `squarified` layout, customize `rectangleAspectRatioPreference` if needed.
The left-to-right order of groups should ideally correspond to the order in the `dataObject`. | Use the `relaxed` layout, set `relaxationInitializer` to `ordered`.
The left-to-right order of groups must correspond to the order in the `dataObject`. | Use the `ordered` layout, set `layoutByWeightOrder` to `false`, customize `rectangleAspectRatioPreference` if needed.
Close-to-round group shapes are preferred. | Use the `relaxed` layout for a polygonal diagram or the `squarified` layout for a rectangular diagram.

Tip

Regardless of the layout you choose, check also the `stacking` option for different ways of arranging hierarchical groups.

Heads up!

Certain other options are applicable only when a specific layout is used. For example, the `relaxationInitializer`, `relaxationVisible` and other relaxation-related options are only meaningful when `layout` is set to `relaxed`.

### layoutByWeightOrder

When `true`, FoamTree will lay out the groups in the order of decreasing weight. When `false`, FoamTree will lay out the groups in the order they were provided in the `dataObject`.

Please see the `layout` option for information about how the order of groups can translate into their position in the visualization.

### stacking

Determines how FoamTree will display nested groups. The following values are supported:

hierarchical

  produces a layered view of the hierarchy. To be able to interact with lower-level groups, the user will need to open the parent group for browsing.

  This is the default stacking mode and it is particularly useful for large deeply-nested hierarchies.

  See the large hierarchy demo for an application of the hierarchical stacking mode.

flattened

  produces a flattened view of the hierarchy. Groups of all levels are immediately available for interaction. This kind of stacking will work especially well with a rectangular `layout`.

  Labels of parent groups are displayed in the dedicated area (towards the top of the polygon in the screenshot). Please see the `descriptionGroup*` options to customize that area. You may also need to implement a `groupLabelLayoutDecorator` to customize the label fitting options when fitting parent group descriptions.

  See the FT500 explorer, SCADA dashboard or the Unit test coverage demo for an applications of the flattened stacking mode.

  Note: With the flattened stacking mode, the default values of the `maxGroupLevelsDrawn`, `maxGroupLabelLevelsDrawn` `maxGroupLevelsAttached` cause FoamTree to display not more than 4 levels of groups. With hierarchical stacking, as the user opens lower-level groups FoamTree automatically computes the layout of the groups on level 5 and lower. This is however not the case in the flattened stacking mode. For this reason, you may need to increase the values of those properties to reveal the lower-level groups. You may also consider using the deferred layout technique to mitigate the delay related to eager computation of the layout for the whole deeply-nested hierarchy.

### descriptionGroup

Determines when to draw an extra group label area.

auto
  The extra label area will be drawn only when `stacking` is set to `flattened`.
always

  The extra label area will be drawn for all groups when `stacking` is set to `flattened`. If `stacking` is set to `hierarchical`, each group that sets its `description` property to `true` will gets its label drawn both on top of the closed group and inside the group.

      foamtree.set({
        descriptionGroup: "always",
        dataObject: {
          groups: [
            {
              label: "Group with a description area", description: true,
              groups: [{}, {}, {}, {}, {}, {}]
            },
            { label: "Group without a description area", groups: [{}, {}, {}] }
          ]
        }
      });

  You can use the extra area to draw longer group descriptions or to mix flattened and hierarchical stacking.

See the `descriptionGroupType` for different description area layout styles.

### descriptionGroupType

Determines how FoamTree will assign space for the group label area in flattened stacking mode or when extra description space is requested by the `descriptionGroup` option. Two values are currently supported:

stab
  Space for the description group is reserved by cutting a horizontal strip off the top or bottom of the group's polygon.
floating
  Space for the description group is reserved among the child groups, following the current `layout`.

Please see the `groupLabelLayoutDecorator` option for a way to customize the label layout for the description group.

### descriptionGroupSize

Note: applicable only when `stacking` is set to `flattened`.

The desired area of the description group, relative to the area of the parent polygon. The value of `1.0` means FoamTree will try to allocate the description group to be half of the area of the polygon.

### descriptionGroupMinHeight

Note: applicable only when `stacking` is set to `flattened` and `descriptionGroupType` is `stab`.

Minimum height of the description group's bounding box, in pixels. Use this option to ensure some minimum height of the description group, so that there's reasonable space to fit the label.

### descriptionGroupMaxHeight

Note: applicable only when `stacking` is set to `flattened` and `descriptionGroupType` is `stab`.

Maximum height of the description group's bounding box, relative to the height of the paren group's bounding box. Use this option to ensure that the description area does not take up too much of the parent polygon's area.

### descriptionGroupPosition

Note: applicable only when `stacking` is set to `flattened`.

Determines the position of the description group inside the parent polygon. The allowed values for this option depend on the current `descriptionGroupType`:

stab
  Two values are allowed: `top` and `bottom`, which will place the description group at the top or bottom of the parent polygon, respectively.
floating
  Please see `attributionPosition` for a description of the allowed values.

### descriptionGroupDistanceFromCenter

Note: applicable only when `stacking` is set to `flattened` and `descriptionGroupType` is `floating`.

Determines the distance of the description group from the center of the parent polygon. Please see `attributionDistanceFromCenter` for a description of the allowed values.

### showZeroWeightGroups

If `true`, groups whose weight is zero will be visible, with their weight slightly smaller than the smallest non-zero weight of the sibling groups.

    foamtree.set({
      showZeroWeightGroups: true,
      dataObject: {
        groups: [
          { label: "Group 1", weight: 1 },
          { label: "Group 2", weight: 1 },
          { label: "Group 3", weight: 1 },
          { label: "Group 4", weight: 0 }
        ]
      }
    });

    foamtree.set({
      showZeroWeightGroups: false,
      dataObject: {
        groups: [
          { label: "Group 1", weight: 1 },
          { label: "Group 2", weight: 1 },
          { label: "Group 3", weight: 1 },
          { label: "Group 4", weight: 0 }
        ]
      }
    });

### groupMinDiameter

Minimum estimated diameter child groups must have in order to be drawn. To avoid illegible small labels and to speed up the rendering, FoamTree can be set to skip drawing child groups if their estimated diameter is smaller than `groupMinDiameter`. Setting the minimum group diameter to 0 will cause FoamTree to draw as many groups as possible groups given the `maxGroups` limit and the available floating point precision.

The following example sets a very large value for the `groupMinDiameter` option after a short delay. Observe that once the change is applied, the child groups disappear – their diameters were smaller than the threshold.

    var timeout = setTimeout(function() {
      foamtree.set({
        groupMinDiameter: 500,

        // Set the data object again to apply the change
        dataObject: foamtree.get("dataObject")
      });
    }, 2000);

### maxGroups

The hard limit on the number of groups FoamTree will attempt to include in a single visualization. The limit is there to prevent excessive memory usage when browsing very deep hierarchies. The limit is applied in a breadth-first fashion, which means that if FoamTree needs to skip groups that do not fit within the limit, it will skip the groups at lower levels of the hierarchy.

### rectangleAspectRatioPreference

Note: applicable only when `layout` is set to `ordered` or `squarified`.

Determines the desired aspect ratio of rectangles produced in the `ordered` and `squarified` layouts.

option value < 0
  FoamTree will try to produce rectangles whose width is larger than the height. Such arrangement will make it slightly easier to fit labels into small rectangular groups.
option value > 0
  FoamTree will prefer rectangles whose width is smaller than the height.
option value == 0
  FoamTree will prefer rectangles of equal width and height.

### geometry

Returns information about the geometry of the polygon representing a group or `null` if the group's polygon is not drawn. The returned object contains the following properties:

polygonCenterX
  the X coordinate of the group's polygon. The coordinate is absolute, it does not take into account the transformations resulting from zooming, panning and exposure.
polygonCenterY
  the Y coordinate of the group's polygon, absolute
polygonArea
  the area of the group's polygon, absolute
boxLeft
  the X coordinate of the top-left corner of the group polygon's bounding box, absolute
boxTop
  the Y coordinate of the top-left corner of the group polygon's bounding box, absolute
boxWidth
  width of the group polygon's bounding box, absolute
boxHeight
  height of the group polygon's bounding box, absolute
labelFontSize
  size of the font used to draw the group's label
labelBoxLeft
  the X coordinate of the top-left corner of the group label's bounding box, absolute
labelBoxTop
  the Y coordinate of the top-left corner of the group label's bounding box, absolute
labelBoxWidth
  width of the group label's bounding box, absolute
labelBoxHeight
  height of the group label's bounding box, absolute
polygon
  an array of objects containing the X and Y coordinates of the groups polygon, in the clockwise order. All polygons produced by FoamTree are convex. The array is only returned if `true` is passed as the third parameter of the `get` method.
neighbors

  Since 3.4.0 an array of references to the group objects corresponding to the neighbors of this group. The array is aligned with the `polygon` array: at an index corresponding to a polygon vertex, it contains the neighbor that shares the edge starting at the vertex and going clockwise. Please note that that some slots in the neighbor array may be `null`, which means the corresponding edge is the boundary of the visualization area. For a potential use case of this property, see the Background demo.

  Note: The neighbors array is available only when the `layout` option is set to `relaxed`. The array is only returned if `true` is passed as the third parameter of the `get` method.

To retrieve the information about a group, provide the individual group selector as the second parameter of the `get` method.

    console.log(foamtree.get("geometry", "1"));

To retrieve the full geometry information, including coordinates of the polygon's vertices, pass `true` as the third parameter of the `get` method:

    console.log(foamtree.get("geometry", "1", true));

Please see the accuracy.html demo for an advanced code example that uses the geometry information to compute the accuracy with which polygon's areas correspond to the group's weights.

### containerCoordinates

Converts the the provided visualization-area-relative point coordinates to the coordinates relative to the HTML container in which FoamTree is embedded. You can use this method to position additional HTML elements over specific shapes drawn inside some FoamTree group and ensure that the position will be correct regardless of zooming, panning and group exposure.

Two arguments are required to retrieve the container-relative coordinates:

- Individual group selector, passed as the second argument to the `get` method, identifying the FoamTree group in which the input point lies. FoamTree group is required because transformations, such as exposure, are applied on per-group basis, so the inverse coordinate transform needs to take into account the scale and offset of the specific transformed group.
- Visualization-area-relative coordinates of the point of interest, as an object with `x` and `y` properties, passed as the third argument to the `get` method. The coordinate space you will use here is the same as the on in which custom group content is drawn in `groupContentDecorator`.

The following example retrieves the container-relative coordinates of the center of some group.

    var geometry = foamtree.get("geometry", "1");
    console.log(foamtree.get("containerCoordinates", "1",
      { x: geometry.polygonCenterX, y: geometry.polygonCenterY }));

Tip: If your goal is to use this property to synchronize the position of some HTML element with a specific point inside FoamTree visualization, the best place to perform the synchronization is the `onRedraw` callback, which is called every time FoamTree is redrawn. This will ensure that you will have the chance to reposition the element on zoom, pan or exposure changes.

Please see the sticky-tooltips.html demo for a real-world usage of this property.

## Relaxation

Options described in this section determine how FoamTree refines the initial polygon arrangement to arrive at a visually pleasing result. Options in this section are only applicable when `layout` is set to `relaxed`.

### relaxationInitializer

Note: applicable only when `layout` is set to `relaxed`.

Determines the initial layout of groups' polygons. The following values are supported:

fisheye
  puts large groups in the center of the visualization area and the smaller groups towards the corners
blackhole
  puts small groups in the center of the visualization and the larger groups towards the corners
ordered
  Since 3.3.0 lays out the groups in a left-to-right lines, in the order the groups were specified in the data object. This initializer creates layouts most similar to the ones generated by FoamTree 2.0.x.
squarified
  puts large groups close to the top-left corner of the visualization and the small groups close to the bottom-right corner.
random
  puts groups at random positions

The example below demonstrates the available initializers. Note that in order to apply a new initializer to the currently visible data, you need to set the `dataObject` option to trigger the layout update.

    foamtree.set({
      dataObject: {
        groups: (function () {
          var arr = [
            { label: "fisheye", weight: 7 }, { label: "blackhole", weight: 6 },
            { label: "ordered", weight: 5 }, { label: "squarified", weight: 5 },
            { label: "random", weight: 4 }
          ];
          for (var i = 0; i < 100; i++) {
            arr.push({
              label: "",
              weight: 0.1 + Math.random() +
                (Math.random() < 0.2 ? Math.random() * 3 : 0)
            });
          }
          return arr;
        })()
      },

      relaxationVisible: true,         // show relaxation
      wireframeLabelDrawing: "always", // show labels during relaxation

      onGroupClick: function (event) {
        event.preventDefault();
        if (event.group.label) {
          this.set("relaxationInitializer", event.group.label);

          // Set the data object again to trigger layout update
          this.set("dataObject", this.get("dataObject"));
        }
      }
    });

Since 3.1.0 You can also specify different layout initializers for each parent group separately by passing the initializer type in the `relaxationInitializer` property of the group's data object.

The following example demonstrates all of the available initializers in one visualization.

    foamtree.set({
      dataObject: {
        groups: (function () {
          var initializers = [ "fisheye", "random", "ordered", "squarified", "blackhole" ];
          return initializers.map(function (i) {
            return {
              relaxationInitializer: i, // pass the initializer to use for this group
              label: i,
              groups: randomGroups(40)
            }
          });
        })()
      },

      relaxationVisible: true,         // show relaxation
      relaxationMaxDuration: 6000,     // make relaxation last longer
      relaxationQualityThreshold: 0.1,
      fadeDuration: 2000,              // use a simple fade-in
      rolloutDuration: 0,
      pullbackDuration: 0,
      wireframeLabelDrawing: "always"  // show labels during animation
    });

### relaxationVisible

Note: applicable only when `layout` is set to `relaxed`.

When set to `true`, FoamTree will show the intermediate steps of layout relaxation. For large data sets, when layout computation may take a few seconds, you may want to show the relaxation process to the users, so that they don't see the blank screen while waiting for the final visualization to be computed.

Layout relaxation and rollout animation can run in parallel. Alternatively, to let the user watch the layout stabilize, you may want to disable the rollout by setting `rolloutDuration` to `0` and fading in the visualization container by setting `fadeDuration` to `1000`, for example.

    foamtree.set({
      dataObject: {
        groups: (function () {
          var arr = [];
          for (var i = 0; i < 100; i++) {
            arr.push({
              label: "",
              weight: 0.1 + Math.random() +
                (Math.random() < 0.2 ? Math.random() * 3 : 0)
            });
          }
          return arr;
        })()
      },

      relaxationInitializer: "random",
      relaxationVisible: true,
      fadeDuration: 1000
    });

When relaxation is visible, you can report the progress of the process to the user using the relaxation progress utility.

Heads up!

When relaxation process is visible, the API-triggered exposure changes of a group will be ignored until the polygon corresponding to that group is computed. In particular, when `relaxationVisible` is `true`, the initial exposure state provided in the `dataObject` will be ignored. As a workaround, you can expose the requested sites when the rollout completes as shown in the following example.

    foamtree.set({
      dataObject: { groups: randomGroups(20) },
      relaxationVisible: true,
      fadeDuration: 1000,
      onRolloutComplete: function(progress, complete) {
        foamtree.expose(foamtree.get("dataObject").groups[0]);
      }
    });

### relaxationMaxDuration

Note: applicable only when `layout` is set to `relaxed`.

Determines the maximum duration of relaxation, in milliseconds. Depending on the complexity of the data set and the `relaxationQualityThreshold`, the relaxation may or may not complete before the specified timeout. If FoamTree needs to stop the relaxation because the alloted time has been exceeded, the quality of the layout may be lowered.

Heads up!

Currently, the `relaxationMaxDuration` is enforced only if `relaxationVisible` is set to `true`.

The following example sets a fairly short maximum relaxation duration. Observe how this lowers the quality of the layout by creating non-round, elongated polygons.

    foamtree.set({
      dataObject: {
        groups: (function () {
          var arr = [];
          for (var i = 0; i < 100; i++) {
            arr.push({
              label: "",
              weight: 0.1 + Math.random() +
                (Math.random() < 0.2 ? Math.random() * 3 : 0)
            });
          }
          return arr;
        })()
      },

      relaxationInitializer: "random",
      relaxationVisible: true,
      relaxationMaxDuration: 100
    });

### relaxationQualityThreshold

Note: applicable only when `layout` is set to `relaxed`.

The desired layout quality to achieve during relaxation. High-quality layouts will consist of "round" polygons, lower-quality layouts will tend to contain more elongated, irregular shapes. The lower the value of the threshold, the higher the quality of the layout and the longer the layout computation time. If `relaxationVisible` is `true`, the desired quality level may not be achieved if the `relaxationMaxDuration` timeout is exceeded.

Setting `relaxationQualityThreshold` to `0` and `relaxationMaxDuration` to `Number.MAX_VALUE` will create the highest-quality layout possible, at the cost of long relaxation time. Setting `relaxationQualityThreshold`, regardless of the relaxation duration timeout will disable the relaxation leading to lower-quality layout, but faster computation. The following examples demonstrate the two opposite situations.

    foamtree.set({
      dataObject: { groups: randomGroups(80) },
      relaxationInitializer: "fisheye",
      relaxationVisible: true,
      relaxationQualityThreshold: Number.MAX_VALUE
    });

    foamtree.set({
      dataObject: { groups: randomGroups(80) },
      relaxationInitializer: "fisheye",
      relaxationVisible: true,
      relaxationMaxDuration: Number.MAX_VALUE,
      relaxationQualityThreshold: 0
    });

### resizeTransform

The transform to apply to polygon centers when resizing the visualization container.

morph
  Transforms the current polygon centers in such a way that their relative positions with respect to the container remain the same after resizing. With `relaxationVisible` set to `true`, this transformation mode ensures smooth polygon transition animations. However, the morphing transformation does not preserve the original positions of the polygons. This means that polygons may gradually "migrate" to arbitrary areas of the visualization as a result of a series of container resizing events.
initialize
  Re-initializes polygon positions from scratch on every container size change. This transformation helps to ensure the polygons remain in the same area of the visualization after multiple resize events. When using this transformation keep `relaxationVisible` set to `false` to avoid jittery polygon animations.

### groupResizingBudget

Note: applicable only when `layout` is set to `relaxed`.

Determines how many times FoamTree should attempt to set the desired size for the groups polygon. Depending on the characteristics of the input data, FoamTree may not be able to set the desired sizes of the polygons right away. Depending on the `groupResizingBudget` value, FoamTree will make several attempts to set the desired size during the course of relaxation.

Setting this option to `0` may cause large-weight groups to be rendered smaller than desired. Setting a large value of this option may lead to "jumpy" and time-consuming relaxation.

## Group borders

Options in this section determine the spacing between group's polygons.

### groupBorderRadius

Determines the amount of rounding to apply to polygon corners. Setting group border radius to `0` will produce straight corners, the value of `1.0` will produce near-to-oval shapes.

### groupBorderWidth

Determines the width of the empty space between the sibling group polygons, in pixels.

Heads up!

Setting `groupBorderWidth` to a value smaller than `groupStrokeWidth``/ 2 + 0.5` will prevent FoamTree from using incremental drawing routines and therefore slow down visualization updates during hover, opening and selection changes.

### groupBorderWidthScaling

The scaling factor to apply when drawing borders of child groups. Border with of a child group will be `groupBorderWidthScaling` smaller than the border width of the immediate parent group. Setting `groupBorderWidthScaling` to 1.0 will draw borders of equal widths on all levels of the group hierarchy.

### groupInsetWidth

Determines the width of the empty space between the parent group's edge and its child groups' edges, in pixels.

### groupBorderRadiusCorrection

The correction factor that ensures that the rounded parent group polygon covers all child group's polygons. For large `groupBorderRadius` values, it may sometimes happen that certain child polygons "stick out" of their parent group. In such cases, you can increase the `groupBorderRadiusCorrection` value to ensure proper rendering.

## Group fill

Options documented in this section determine how FoamTree will fill the polygons.

### groupFillType

Determines the way the group polygons should be filled. The following values are possible:

none
  the polygons will not be filled at all
plain
  the polygons will be filled with one color; fast to render
gradient
  the polygons will be filled with a two-color radial gradient; slower to render

Heads up!

On slower devices and/or larger data sets, FoamTree may automatically disable rendering of gradients to improve the rendering performance. To force the rendering of gradients in such cases, increase `wireframeDrawMaxDuration`, `finalCompleteDrawMaxDuration` and `finalIncrementalDrawMaxDuration`.

### groupFillGradientRadius

Determines the radius of the group-filling gradient.

### groupFillGradientCenterHueShift

The amount of hue to add or subtract from the base group color hue to create the polygon-center end of the fill gradient.

### groupFillGradientCenterSaturationShift

The amount of saturation to add or subtract from the base group color saturation to create the polygon-center end of the fill gradient.

### groupFillGradientCenterLightnessShift

The amount of lightness to add or subtract from the base group color lightness to create the polygon-center end of the fill gradient.

### groupFillGradientRimHueShift

The amount of hue to add or subtract from the base group color hue to create the polygon-edge end of the fill gradient.

### groupFillGradientRimSaturationShift

The amount of saturation to add or subtract from the base group color saturation to create the polygon-edge end of the fill gradient.

### groupFillGradientRimLightnessShift

The amount of lightness to add or subtract from the base group color lightness to create the polygon-edge end of the fill gradient.

## Group stroke

Options documented in this section determine how FoamTree will draw the edges of the polygons.

### groupStrokeType

Determines the way the group polygons edges should be drawn. The following values are possible:

none
  the edges will not be drawn at all; fastest to render
plain
  the edges will be drawn using one color; slower to render
gradient
  the edges will be drawn using a two-color linear gradient; slowest to render

Heads up!

On slower devices and/or larger data sets, FoamTree may automatically disable rendering of edges to improve the rendering performance. To force the rendering of gradients in such cases, increase `wireframeDrawMaxDuration`, `finalCompleteDrawMaxDuration` and `finalIncrementalDrawMaxDuration`.

### groupStrokeWidth

Determines the width of the group's edge, in pixels.

Heads up!

Setting `groupStrokeWidth` to a value larger than `(``groupBorderWidth``- 0.5) * 2` will prevent FoamTree from using incremental drawing routines and therefore slow down visualization updates during hover, opening and selection changes.

### groupStrokePlainHueShift

The amount of hue to add or subtract from the base group color hue to create the color used for drawing the plain group's edge.

### groupStrokePlainSaturationShift

The amount of saturation to add or subtract from the base group color saturation to create the color used for drawing the plain group's edge.

### groupStrokePlainLightnessShift

The amount of lightness to add or subtract from the base group color lightness to create the color used for drawing the plain group's edge.

### groupStrokeGradientRadius

The size of the linear gradient used to draw the polygon's edges, in relation to the polygon's size.

### groupStrokeGradientAngle

The angle of the linear gradient used to draw the polygon's edges.

### groupStrokeGradientUpperHueShift

The amount of hue to add or subtract from the base group color hue to create the upper-end color of the polygon edge's gradient.

### groupStrokeGradientUpperSaturationShift

The amount of saturation to add or subtract from the base group color saturation to create the upper-end color of the polygon edge's gradient.

### groupStrokeGradientUpperLightnessShift

The amount of lightness to add or subtract from the base group color lightness to create the upper-end color of the polygon edge's gradient.

### groupStrokeGradientLowerHueShift

The amount of hue to add or subtract from the base group color hue to create the lower-end color of the polygon edge's gradient.

### groupStrokeGradientLowerSaturationShift

The amount of saturation to add or subtract from the base group color saturation to create the lower-end color of the polygon edge's gradient.

### groupStrokeGradientLowerLightnessShift

The amount of lightness to add or subtract from the base group color lightness to create the lower-end color of the polygon edge's gradient.

## Group selection

Options documented in this section determine how FoamTree will draw and decorate the selected groups.

### groupSelectionOutlineColor

Color of the outline stroke for the selected groups.

### groupSelectionOutlineWidth

Width of the selection outline to draw around selected groups.

Heads up!

Setting `groupSelectionOutlineWidth` to a value larger than

`(``groupBorderWidth``-``groupSelectionOutlineShadowSize``- 0.5) * 2`

will prevent FoamTree from using incremental drawing routines and therefore slow down visualization updates during hover, opening and selection changes when one ore more groups are selected.

### groupSelectionOutlineShadowSize

The size of the drop shadow to apply to the selection outline.

Heads up!

Setting `groupSelectionOutlineShadowSize` to a value larger than

`(``groupBorderWidth``-``groupSelectionOutlineWidth``- 0.5) * 2`

will prevent FoamTree from using incremental drawing routines and therefore slow down visualization updates during hover, opening and selection changes when one ore more groups are selected.

### groupSelectionOutlineShadowColor

The color of the selection outline shadow.

### groupSelectionFillHueShift

The amount of hue to add or subtract from the base fill color hue when the group is selected.

### groupSelectionFillSaturationShift

The amount of saturation to add or subtract from the base fill color saturation when the group is selected.

### groupSelectionFillLightnessShift

The amount of lightness to add or subtract from the base fill color lightness when the group is selected.

### groupSelectionStrokeHueShift

The amount of hue to add or subtract from the base stroke color hue when the group is selected.

### groupSelectionStrokeSaturationShift

The amount of saturation to add or subtract from the base stroke color saturation when the group is selected.

### groupSelectionStrokeLightnessShift

The amount of lightness to add or subtract from the base stroke color lightness when the group is selected.

## Group hover

Options documented in this section determine how FoamTree will highlight the hovered-on groups.

### groupHoverFillHueShift

The amount of hue to add or subtract from the base fill color hue when the group is hovered on.

### groupHoverFillSaturationShift

The amount of saturation to add or subtract from the base fill color saturation when the group is hovered on.

### groupHoverFillLightnessShift

The amount of lightness to add or subtract from the base fill color lightness when the group is hovered on.

### groupHoverStrokeHueShift

The amount of hue to add or subtract from the base stroke color hue when the group is hovered on.

### groupHoverStrokeSaturationShift

The amount of saturation to add or subtract from the base stroke color saturation when the group is hovered on.

### groupHoverStrokeLightnessShift

The amount of lightness to add or subtract from the base stroke color lightness when the group is hovered on.

## Group labels

Options documented in this section determine how FoamTree will lay out and draw group labels.

### groupLabelFontFamily

Font family to use for drawing group labels. CSS-compliant font family specifications are supported, including webfonts imported using the `@font-face` syntax.

### groupLabelFontStyle

Font style to use for drawing group labels. All CSS-compliant font styles are allowed, such as `italic`.

### groupLabelFontWeight

Font weight to use for drawing group labels. All CSS-compliant font weights are allowed, such as `bold`.

### groupLabelFontVariant

Font variant to use for drawing group labels. All CSS-compliant font variants are allowed, such as `small-caps`.

### groupLabelMinFontSize

Minimum font size used for drawing group labels, in pixels.

Note that setting minimum font size to a large value may result in many labels being replaced with an ellipsis ("dots"), as in this example:

    foamtree.set({
      dataObject: largeDataSet,
      groupLabelMinFontSize: 30,
      groupLabelMaxFontSize: 40
    });

To force FoamTree to draw labels for all groups, set `groupLabelMinFontSize` to `0`. This setting, however, may significantly slow down visualization rendering.

### groupLabelMaxFontSize

Maximum font size used for drawing group labels. See the `groupLabelMinFontSize` attribute for details.

### groupLabelDecorator

Allows to customize or completely replace the default text of group labels. When labels are drawn, FoamTree will call the provided function once for each group (parent groups will be processed before children groups). The task of the function is to modify or completely replace the default label text. The custom label text will most likely be based on custom properties passed along with the data model.

The signature of the decorator callback should be the following:

`function (options, properties, variables)`

where:

options
  all current visualization options (keys and values).
properties

  an object with properties describing the group being decorated. The object will be a union of objects retrieved from the `hierarchy`, `state` and `geometry` options.

variables

  object with label-related variables this function can change.

  The following variables are available:

  labelText
    the default text of the label, equal to the `label` property of the group's data object.

Heads up!

The limited demo version of FoamTree will not call this decorator for the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

The following example upper cases all group and adds angle quotes around them. Note how the quotes are separated from the main label by the non-breakable space unicode to ensure the quotes are not left as the only characters in the line. See the documentation for the `dataObject` option for a list of other special characters supported by FoamTree.

    foamtree.set({
      groupLabelDecorator: function(opts, props, vars) {
        vars.labelText = "«\u00a0" + vars.labelText.toLocaleUpperCase() + "\u00a0»";
      }
    });

### groupLabelLineHeight

The line height to use when rendering labels in multiple lines.

### groupLabelHorizontalPadding

The padding to add on the left and right of the label, in current font size unit.

### groupLabelVerticalPadding

The padding to add on the top and bottom of the label, in current font size unit.

### groupLabelMaxTotalHeight

The maximum total height of the label in relation to the height of the group's polygon.

### groupLabelUpdateThreshold

Every time the geometry of the group's polygon changes, FoamTree needs to decide whether to re-layout the label text. The `groupLabelUpdateThreshold` determines how much must the area of the polygon change for FoamTree to re-layout the label. For example, if the value of the threshold is `0.05`, FoamTree will re-layout the label when the area of the polygon is larger or smaller by 5% or more.

Setting `groupLabelUpdateThreshold` to `0` will cause FoamTree to re-layout the label on every group polygon change. Depending on the size of the data set, it may significantly slow down visualization rendering when `relaxationVisible` is `true`.

### groupLabelColorThreshold

Picks the label color automatically depending on the group's color brightness. If the brightness is below this threshold `groupLabelLightColor` is used, otherwise `groupLabelDarkColor` is used. Setting the threshold to either 0 or 1 will pick dark or light labels, correspondingly.

Automatic choice of label colors is also provided as part of `groupColorDecorator`'s functionality.

### groupLabelDarkColor

The label color to use on bright groups (see `groupLabelColorThreshold`).

### groupLabelLightColor

The label color to use on dark groups (see `groupLabelColorThreshold`).

## Group exposure

Options described in this section determine how FoamTree draws and decorates exposed groups.

### groupExposureScale

The magnification scale at which exposed groups will be drawn. If you set `groupExposureScale` to `1.0`, exposed groups will be drawn at the same scale as the unexposed groups.

### groupExposureShadowColor

The color of drop shadow to apply to exposed groups.

### groupExposureShadowSize

The size of drop shadow to apply to exposed groups, in pixels.

### groupExposureZoomMargin

The margin size to apply when zooming to the exposed group, relative to the group polygon's bounding box dimensions. If `groupExposureZoomMargin` is `0`, the exposed group will occupy the full height / width of the view port. For larger values, a zoom margin will be added around the exposed group. The larger the value, the larger margin will be applied.

### groupUnexposureLightnessShift

The amount of lightness to add or subtract from the groups base color's lightness when the group is unexposed.

### groupUnexposureSaturationShift

The amount of saturation to add or subtract from the groups base color's saturation when the group is unexposed.

### groupUnexposureLabelColorThreshold

The `labelColorThreshold` value to use for unexposed groups. When the colors of unexposed groups are significantly modified using the `groupUnexposureLightnessShift` and `groupUnexposureSaturationShift` options, you may want to modify this threshold to, for example, always draw the labels of unexposed groups in the dark color.

### exposeDuration

The duration of the group exposure and unexposure animation.

### exposeEasing

The easing function to use during group exposure and unexposure animations. Meaningful only when `exposeDuration` is larger than `0`.

## Group opening/closing

### openCloseDuration

The duration of the group opening or closing animation.

## Group hierarchy

Options in this group determine how the stacked polygons representing the hierarchy of groups will be drawn.

### parentFillOpacity

The opacity to use when filling groups that have child groups. By default, the parent group is drawn semi-transparent to indicate that the group has child groups that can be browsed. If you set `parentFillOpacity` to `1.0`, the parent group will be fully opaque and the child groups will not be visible until the group gets open. If you set `parentFillOpacity` to `0.0`, the parent group's polygon will not be filled at all and the child groups will be fully visible at all times.

### parentStrokeOpacity

The opacity to use when drawing strokes of groups that have child groups.

### parentLabelOpacity

The opacity to use when drawing labels of groups that have child groups.

### parentOpacityBalancing

When set to `true`, FoamTree will try to equalize the opacity of groups' polygons, so that the aggregated opacity of, for example, a group with 3 levels of child groups and a group without child groups will be roughly the same. Opacity equalization will usually lead to a more uniform look of the whole visualization area.

Opacity balancing will only be applied when the base color of the group is not fully opaque, that is when its alpha component is less than 1.

## Group colors

This section describes options that determine the base colors assigned for groups.

### rainbowStartColor

Start color to use if rainbow color model is used for coloring groups. The rainbow color model will radially or linearly spread the color hue among top-level groups, starting at `rainbowStartColor` and ending at `rainbowEndColor`. Sub-groups will be painted with the parent group's hue but with varying degrees of saturation and lightness.

For custom coloring of groups, use `groupColorDecorator`.

### rainbowEndColor

End color to use if rainbow color model is used for coloring groups. See `rainbowStartColor` for a description of how the rainbow color model works.

### rainbowColorDistribution

Determines how the rainbow colors will be distributed. You can use one of the following values:

radial
  distributes the colors radially around the center of the visualization area
linear
  distributes the colors linearly across the whole visualization area

You can use the `rainbowColorDistributionAngle` option to further tune the color distribution.

### rainbowColorDistributionAngle

When `rainbowColorDistribution` is `radial`, determines the angle at which the rainbow color will start. Change the the values of this option, to rotate the rainbow around the central point of the visualization area.

When `rainbowColorDistribution` is `linear`, determines the angle of the linear gradient formed by the rainbow color transition.

### rainbowLightnessDistributionAngle

Determines the angle of the linear gradient formed by the color lightness variations created for lower-level groups.

### rainbowSaturationCorrection

When `rainbowColorDistribution` is `radial`, FoamTree can desaturate colors of the polygons lying close to the center of the visualization area to make the color transitions smoother.

You can use the `rainbowLightnessCorrection` option to vary the strength of this correction. The value of `0` will moderately decrease the saturation, the value of `-1` will strongly desaturate the center groups. Finally, set the correction to `1` to disable correction decreases completely.

### rainbowLightnessCorrection

When `rainbowColorDistribution` is `radial`, FoamTree can adjust the lightness of colors of the polygons lying close to the center of the visualization area to make the color transitions smoother.

You can use the `rainbowLightnessCorrection` option to vary the strength of this adjustment. The value of `0` means no adjustment. The value of `-1` will strongly decrease the lightness of the center groups. Finally, the value of `1` will strongly increase the lightness of the central groups.

### rainbowLightnessShift

Determines the strength of the color lightness variations applied to lower-level groups.

### rainbowLightnessShiftCenter

Determines the central lightness value around which the lightness variations will be applied for the lower-level groups.

### groupColorDecorator

A callback function you can use to customize or completely replace the default rainbow color model for groups. During each redraw, the visualization code will call the provided function once for each group. The task of the function is to modify or completely replace the default color of the group and its label. New colors can be derived from various properties of the group, such as its nesting level, number of siblings, or custom properties passed along with the data model.

The signature of the decorator callback should be the following:

`function (options, properties, variables)`

where:

options
  all current visualization options (keys and values).
properties
  an object with properties describing the group being decorated. The object will be a union of objects retrieved from the `hierarchy`, `state` and `geometry` options.
variables

  an object with two properties: `groupColor`, containing the group color computed by the default rainbow color model, and `labelColor` always equal to `auto`. The callback function can either change some of the properties of `groupColor` or replace any of the colors with a new object representing the color to use.

  The callback function can replace these values with a CSS3 color string (`hsl`, `hsla`, `rgb` or `rgba` color specification) or an object containing the following properties:

  - `model`: `rgba` for the RGBA model, `hsla` for the HSLA model. When adding or changing the values of other properties, it is important to set this property to reflect the desired color model.
  - `r`, `g`, `b`, `a`: RGBA values (integers in the 0..255 range).
  - `h`, `s`, `l`, `a`: HSLA values, hue is an angle (0..359), saturation and lightness are percentages (0..100) and transparency is a range between 0 and 1.

  The default values computed by the rainbow color model are always passed to the callback in the HSLA format described above.

  The `labelColor` variable can be set to a string value of `auto` which will recompute the label color again depending on the updated `groupColor` and visualization options controlling dark/ light label colors: `labelColorThreshold`, `labelLightColor` and `labelDarkColor`.

Heads up!

The limited demo version of FoamTree will not call this decorator for the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

#### Colors defined in the data model

The simplest color model could just use explicit color values provided as part of the data model.

    foamtree.set({
      dataObject: {
        groups: [
          { label: "C", color: "#00aeef" },
          { label: "M", color: "#ec008c" },
          { label: "Y", color: "#fff200" },
          { label: "K", color: "#231f20" }
        ]
      },
      groupColorDecorator: function (opts, params, vars) {
        vars.groupColor = params.group.color;
        vars.labelColor = "auto";
      }
    });

#### Colors depending on group state

By default, FoamTree modifies the base group color depending on the state of the group. This behaviour is determined by such properties as `groupSelectionStrokeLightnessShift` or `groupHoverFillLightnessShift` and usually makes the selected or hovered-on group lighter. To achieve even greater customization, you can use the group state information in the decorator:

    foamtree.set({
      dataObject: {
        groups: [
          { label: "C", color: "#00aeef" },
          { label: "M", color: "#ec008c" },
          { label: "Y", color: "#fff200" },
          { label: "K", color: "#231f20" }
        ]
      },
      groupColorDecorator: function (opts, params, vars) {
        if (params.selected) {
          vars.groupColor = "#fff";
          vars.labelColor = params.group.color;
        } else {
          vars.groupColor = params.group.color;
          vars.labelColor = "#fff";
        }
      }
    });

#### Colors derived from custom properties of the data model

Most commonly, polygon colors will depend on some domain-specific properties of the groups. For instance, if FoamTree is used to visualize a group of news stories and each story has been tagged with sentiment information (positive, negative, neutral), the color could reflect the sentiment. In the example shown below, we draw positive news in green, negative in red and neutral in grey.

    foamtree.set({
      groupColorDecorator: function(opts, params, vars) {
        // Sentiment is a custom property with values in the -1..+1 range.
        // -1 means negative sentiment, +1 -- positive, 0 -- neutral.
        var sentiment = params.group.sentiment;
        if (sentiment === undefined) {
          // Make neutral groups grey
          vars.groupColor.s = 0;
          vars.groupColor.l = 0;
          vars.groupColor.a = 0.2;
        } else {
          if (sentiment > 0) {
            // Make positive groups green
            vars.groupColor.h = 120;
          } else {
            // Make negative groups red
            vars.groupColor.h = 0;
          }

          // Make saturation and lightness depend on
          // the strength of the sentiment.
          vars.groupColor.s = 50 * (1 + Math.abs(sentiment));
          vars.groupColor.l = Math.abs(60 * sentiment);
          vars.groupColor.a = 0.5;
        }
        // Indicate that we use the HSLA model
        vars.groupColor.model = "hsla";
      },
      dataObject: {
        groups: [
          { label: "Bad news", groups: [
            { label: "Last penny lost", sentiment: -0.5 },
            { label: "Bazinga doomed",  sentiment: -1 }
            ]},
          { label: "Good news", groups: [
            { label: "iPads under $100",      sentiment: 0.5 },
            { label: "Martians are friendly", sentiment: 1 }
            ]},
          { label: "Other news", groups: [
            { label: "Vampires on the loose", sentiment: 0 },
            { label: "Van Helsing to the rescue", sentiment: 0 }
            ]}
        ]
      }
    });

#### Animated colors

Finally, you can create an animated color model by making the color decorator a time-dependant function and periodically redrawing the visualization using the `redraw` method. The example below creates a simple radar-like animation. For faster drawing, we set `groupFillType` to `plain` and `groupStrokeType` to `none`.

    running = false;

    var frame = 0;
    var running = true;
    foamtree.set({
      groupColorDecorator: function(opts, params, vars) {
        // We change the color only for top-level groups. For child groups,
        // FoamTree will generate brightness variations of the parent group's color.
        if (params.level == 0) {
          var delay = 5;
          var count = 3;
          var frameDiv = Math.floor(frame / delay);
          var mod = frameDiv % params.siblingCount;
          var diff = (mod - params.index + params.siblingCount)
                       % params.siblingCount;
          if (diff < count) {
            vars.groupColor.g = 255 *
              (1 - ((diff * delay + frame % delay) / (delay * count)));
          } else {
            vars.groupColor.g = 40;
          }
          vars.groupColor.r = 40;
          vars.groupColor.b = 40;
          vars.groupColor.model = "rgb";
        }
      },
      dataObject: {
        groups: (function() {
          var arr = [];
          for (var i = 0; i < 50; i++) {
            arr.push({
              label: "",
              weight: Math.pow(Math.random(), 3) * 0.8 + 0.2,
              groups: [ { label: "" }, { label: "" }, { label: "" }]
            });
          }
          return arr;
        })()
      },
      groupFillType: "plain",
      groupStrokeType: "none"
    });

    // Redraw the visualization periodically. Although we use setTimeout()
    // here, use the requestAnimationFrame() when possible.
    setTimeout(function redraw() {
      if (!running) { // stopped?
        return;
      }
      foamtree.redraw(true);
      frame++;
      setTimeout(redraw, 16); // next frame
    }, 16);

## Rollout

You can use options described in this section to change the animation FoamTree uses to show a new data set. By changing various rollout properties you can create many different animation styles.

### rolloutStartPoint

The point in the visualization container area at which the rollout will start. The following start point values are allowed:

center
  rollout will start from the group lying closest to the center of the visualization container
topleft
  rollout will start from the group lying closest to the top-left corner of the visualization container
bottomright
  rollout will start from the group lying closest to the bottom-right corner of the visualization container
random
  rollout will start from a random group

### rolloutMethod

FoamTree will start the rollout animation from the group determined using the `rolloutStartPoint` option. Then, further polygons will be included in the animation, depending on the value of this option:

groups

  on each animation step, all neighbors of the groups already being animated will be included in the animation, which will create a sort of ripple effect as shown in the following example.

      foamtree.set({
        dataObject: { groups: randomGroups(200) },

        // Roll out in groups
        rolloutMethod: "groups",

        // Create a gentle scaling effect
        rolloutDuration: 6000,
        rolloutEasing: "bounce",
        rolloutScalingStrength: -0.4,
        rolloutRotationStrength: 0,
        rolloutPolygonDrag: 0.08
      });

individual

  on each animation step only one neighbor of the groups already being animated will be included in the animation. When using this rollout method, you may want to decrease `rolloutPolygonDrag` to make the animation progress smoother.

      foamtree.set({
        dataObject: { groups: randomGroups(200) },

        // Roll out each group individually
        rolloutMethod: "individual",

        // Create a gentle scaling effect
        rolloutDuration: 4000,
        rolloutEasing: "bounce",
        rolloutScalingStrength: -0.4,
        rolloutRotationStrength: 0,
        rolloutPolygonDrag: 0.015
      });

### rolloutDuration

The duration of the rollout animation at one level of the hierarchy, in milliseconds. The duration of the complete animation is variable and depends on the number of hierarchy levels and the `rolloutChildGroupsDrag` and `rolloutChildGroupsDelay` options.

### rolloutEasing

The animation easing function to use during rollout animation.

### rolloutScalingStrength

Determines the initial scale to set for each polygon at the start of the rollout. During the rollout animation the initial scale will be transitioned to the neutral value.

When `rolloutScalingStrength` is `-1`, the initial scale will be minus infinity making the polygon appear at infinite distance in front of the user. When `rolloutScalingStrength` is `1`, the initial scale will be plus infinity making the polygon appear at infinite distance behind the user. All intermediate values are allowed and will set the initial scale to the appropriate proportion between the extremes. When `rolloutScalingStrength` is `0`, the initial scale will be equal to the neutral scale.

The following examples demonstrate the rollout animation for negative and positive values of the `rolloutScalingStrength` option. Both examples set `rolloutTransformationCenter` to `0` so that the origin of the scaling is the center of the visualization container.

    foamtree.set({
      dataObject: { groups: randomGroups(100) },

      rolloutScalingStrength: -0.5,

      rolloutDuration: 4000,
      rolloutRotationStrength: 0,
      rolloutTransformationCenter: 0
    });

    foamtree.set({
      dataObject: { groups: randomGroups(100) },

      rolloutScalingStrength: 1,

      rolloutDuration: 4000,
      rolloutRotationStrength: 0,
      rolloutTransformationCenter: 0
    });

Heads up!

When visualizing hierarchical models, setting a positive `rolloutScalingStrength` may create unpleasant effects because child groups will be drawn on top of their parents during the animation as shown in the following example. For this reason, you may want to use negative scaling strengths for hierarchical data.

    foamtree.set({
      dataObject: largeDataSet,

      rolloutScalingStrength: 0.5,

      rolloutDuration: 4000,
      rolloutRotationStrength: 0,
      rolloutTransformationCenter: 0
    });

### rolloutTranslationXStrength

Determines the initial horizontal translation to apply to each polygon at the start of the rollout. The rollout animation will transition the initial translation to the neutral value. The unit of the translation is the width of the parent polygon's bounding box. Fox example, the value of `-0.5` means the translation to the left by half of the width of the parent box.

### rolloutTranslationYStrength

Determines the initial vertical translation to apply to each polygon at the start of the rollout. The rollout animation will transition the initial translation to the neutral value. The unit of the translation is the height of the parent polygon's bounding box. Fox example, the value of `0.5` means the translation to the bottom by half of the height of the parent box.

### rolloutRotationStrength

Determines the initial rotation to apply to each polygon at the start of the rollout. The rollout animation will transition the initial rotation to the neutral value. Rotation of `1.0` means 180 degrees clockwise.

### rolloutTransformationCenter

Determines the point relative to which the rollout transformations will be taken. The value of `0.0` means the center of the parent group's polygon, `1.0` means the center of the currently animated polygon. Intermediate values set the transformation center at the appropriate linear combination of the above extremes.

The following examples demonstrate the difference between the `rolloutTransformationCenter` extremes.

    foamtree.set({
      dataObject: { groups: randomGroups(100) },

      rolloutTransformationCenter: 0,

      rolloutDuration: 4000,
      rolloutRotationStrength: -0.3,
      rolloutScalingStrength: 0,
      rolloutEasing: "squareInOut"
    });

    foamtree.set({
      dataObject: { groups: randomGroups(100) },

      rolloutTransformationCenter: 1,

      rolloutDuration: 4000,
      rolloutRotationStrength: -0.3,
      rolloutScalingStrength: 0,
      rolloutEasing: "squareInOut"
    });

### rolloutPolygonDrag

The amount of delay to apply before starting the rollout of subsequent groups on the same level of hierarchy. The delay will be computed by multiplying `rolloutDuration` by the value of this option.

### rolloutPolygonDuration

Determines the duration of the polygon rollout animation, as a fraction of `rolloutDuration`.

### rolloutLabelDelay

Determines the delay to apply before showing the group's label during rollout, as a fraction of `rolloutDuration`.

Note that when `wireframeLabelDrawing` is set to `never`, group labels will not be drawn at all during rollout. When `wireframeLabelDrawing` is set to `auto`, labels may or may not be drawn during the rollout, depending on the performance of the device and the size of the data set. To force FoamTree to draw labels during the rollout animation, set `wireframeLabelDrawing` to `always`.

### rolloutLabelDrag

The amount of delay to apply before revealing the labels of subsequent groups on the same level of hierarchy. The delay will be computed by multiplying `rolloutDuration` by the value of this option.

### rolloutLabelDuration

Determines the duration of the label fading-in animation, as a fraction of `rolloutDuration`.

### rolloutChildGroupsDelay

The amount of delay to apply before starting the rollout of child groups of a parent group, as a fraction of `rolloutDuration`. If you set `rolloutChildGroupsDelay` to `0`, groups at all levels will start their rollout animations at the same time.

### rolloutChildGroupsDrag

The amount of delay to apply before starting the rollout of child groups of subsequent parent groups groups on the same level of hierarchy. The delay will be computed by multiplying `rolloutDuration` by the value of this option.

## Pullback

You can use options described in this section to change the animation FoamTree uses to hide the current data set before showing a new one. By changing various rollout properties you can create many different animation styles. Many of the pullback options are similar to their rollout counterparts.

### pullbackStartPoint

The point in the visualization container area at which the pullback will start. The following start point values are allowed:

center
  pullback will start from the group lying closest to the center of the visualization container
topleft
  pullback will start from the group lying closest to the top-left corner of the visualization container
bottomright
  pullback will start from the group lying closest to the bottom-right corner of the visualization container
random
  pullback will start from a random group

### pullbackMethod

FoamTree will start the pullback animation from the group determined using the `pullbackStartPoint` option. Then, further polygons will be included in the animation, depending on the value of this option:

groups

  on each animation step, all neighbors of the groups already being animated will be included in the animation, which will create a sort of ripple effect as shown in the following example.

      // Set initial data
      foamtree.set({
        dataObject: { groups: randomGroups(200) }
      });

      // Pull back after a while
      var timeout = setTimeout(function() {
        foamtree.set({
          dataObject: null,

          // Roll out in groups
          pullbackMethod: "groups",

          // Create a gentle scaling effect
          pullbackDuration: 3000,
          pullbackEasing: "quadOut",
          pullbackScalingStrength: -0.4,
          pullbackRotationStrength: 0,
          pullbackPolygonDrag: 0.08
        });
      }, 1000);

individual

  on each animation step only one neighbor of the groups already being animated will be included in the animation. When using this rollout method, you may want to decrease `rolloutPolygonDrag` to make the animation progress smoother.

      // Set initial data
      foamtree.set({
        dataObject: { groups: randomGroups(200) }
      });

      // Pull back after a while
      var timeout = setTimeout(function() {
        foamtree.set({
          dataObject: null,

          // Roll out in groups
          pullbackMethod: "individual",

          // Create a gentle scaling effect
          pullbackDuration: 3000,
          pullbackEasing: "quadOut",
          pullbackScalingStrength: -0.4,
          pullbackRotationStrength: 0,
          pullbackPolygonDrag: 0.015
        });
      }, 1000);

### pullbackDuration

The duration of the pullback animation, in milliseconds.

### pullbackEasing

The animation easing function to use during pullback animation.

### pullbackScalingStrength

Determines the target scale for each polygon to achieve at the end of the pullback animation.

When `pullbackScalingStrength` is `-1`, the target scale will be minus infinity making the polygon appear at infinite distance in front of the user. When `pullbackScalingStrength` is `1`, the target scale will be plus infinity making the polygon appear at infinite distance behind the user. All intermediate values are allowed and will set the target scale to the appropriate proportion between the extremes. When `pullbackScalingStrength` is `0`, the target scale will be equal to the neutral scale.

The following examples demonstrate the pullback animation for negative and positive values of the `pullbackScalingStrength` option. Both examples set `pullbackTransformationCenter` to `0` so that the origin of the scaling is the center of the visualization container.

    // Set initial data
    foamtree.set({
      dataObject: { groups: randomGroups(200) }
    });

    // Pull back after a while
    var timeout = setTimeout(function() {
      foamtree.set({
        dataObject: null,

        pullbackScalingStrength: -0.5,

        pullbackDuration: 2500,
        pullbackEasing: "quadIn",
        pullbackRotationStrength: 0,
        pullbackTransformationCenter: 0
      });
    }, 1000);

    // Set initial data
    foamtree.set({
      dataObject: { groups: randomGroups(200) }
    });

    // Pull back after a while
    var timeout = setTimeout(function() {
      foamtree.set({
        dataObject: null,

        pullbackScalingStrength: 0.5,

        pullbackDuration: 2500,
        pullbackEasing: "quadOut",
        pullbackRotationStrength: 0,
        pullbackTransformationCenter: 0
      });
    }, 1000);

### pullbackTranslationXStrength

Determines the target horizontal translation for each polygon to achieve at the end of the pullback animation. The unit of the translation is the width of the parent polygon's bounding box. Fox example, the value of `-0.5` means the translation to the left by half of the width of the parent box.

### pullbackTranslationYStrength

Determines the target vertical translation for each polygon to achieve at the end of the pullback animation. The unit of the translation is the height of the parent polygon's bounding box. Fox example, the value of `0.5` means the translation to the bottom by half of the height of the parent box.

### pullbackRotationStrength

Determines the target rotation for each polygon to achieve at the end of the pullback animation. Rotation of `1.0` means 180 degrees clockwise.

### pullbackTransformationCenter

Determines the point relative to which the pullback transformations will be taken. The value of `0.0` means the center of the parent group's polygon, `1.0` means the center of the currently animated polygon. Intermediate values set the transformation center at the appropriate linear combination of the above extremes.

The following examples demonstrate the difference between the `pullbackTransformationCenter` extremes.

    // Set initial data
    foamtree.set({
      dataObject: { groups: randomGroups(100) }
    });

    // Pull back after a while
    var timeout = setTimeout(function() {
      foamtree.set({
        dataObject: null,

        pullbackTransformationCenter: 0,

        pullbackDuration: 4000,
        pullbackRotationStrength: -0.3,
        pullbackScalingStrength: 0,
        pullbackEasing: "cubicInOut"
      });
    }, 1000);

    // Set initial data
    foamtree.set({
      dataObject: { groups: randomGroups(100) }
    });

    // Pull back after a while
    var timeout = setTimeout(function() {
      foamtree.set({
        dataObject: null,

        pullbackTransformationCenter: 1,

        pullbackDuration: 4000,
        pullbackRotationStrength: -0.3,
        pullbackScalingStrength: 0,
        pullbackEasing: "cubicInOut"
      });
    }, 1000);

### pullbackPolygonDelay

The pullback animation will first start hiding groups labels and after a delay the hiding of the polygons will start. This option determines the delay to apply before hiding of the polygons starts. The delay is specified as a fraction of `pullbackDuration`.

### pullbackPolygonDrag

The amount of delay to apply before starting the pullback of subsequent groups on the same level of hierarchy. The delay will be computed by multiplying `pullbackDuration` by the value of this option.

### pullbackPolygonDuration

Determines the duration of the polygon pullback animation, as a fraction of `pullbackDuration`.

### pullbackLabelDelay

Determines the delay to apply before hiding group labels during pullback, as a fraction of `pullbackDuration`.

### pullbackLabelDrag

The amount of delay to apply before hiding the labels of subsequent groups on the same level of hierarchy. The delay will be computed by multiplying `pullbackDuration` by the value of this option.

### pullbackLabelDuration

Determines the duration of the label fading-out animation, as a fraction of `pullbackDuration`.

### pullbackChildGroupsDuration

Determines the duration of the sub-group fading-out animation, as a fraction of `pullbackDuration`.

### pullbackChildGroupsDelay

Determines the delay to apply before hiding sub-groups during pullback, as a fraction of `pullbackDuration`.

### pullbackChildGroupsDrag

The amount of delay to apply before hiding the subgroups of subsequent parent groups on the same level of hierarchy. The delay will be computed by multiplying `pullbackDuration` by the value of this option.

## Fading

When `rolloutDuration` or `pullbackDuration` is `0`, FoamTree will use a simple fade in/out animation to show and hide the visualization. You can use options described in this section to customize the fading.

### fadeDuration

The duration of the fade-in/out animation, in milliseconds. The fading in/out is applied only when `rolloutDuration` / `pullbackDuration` is `0`, otherwise the value of this option is ignored. Set this option to `0` to disable fading.

### fadeEasing

The easing function to use when performing the fading animation.

## Zoom

Options in this section customize the way the visualization can be zoomed using the mouse wheel.

### zoomMouseWheelFactor

The magnification factor to apply when the user zooms in using the mouse wheel. When zooming out, the factor will be `1 /``zoomMouseWheelFactor`.

### zoomMouseWheelDuration

The duration of the zoom-in/out animation, in milliseconds.

### zoomMouseWheelEasing

The easing function to apply to the zoom animation.

## Title bar

### titleBarFontFamily

Font family for the title bar, if shown. CSS-compliant font family specifications are supported, including webfonts imported using the `@font-face` syntax. If not specified, the same font as specified for the `groupLabelFontFamily` will be used.

This example renders the title bar in monospace font.

    foamtree.set({
      dataObject: { groups: [
        { label: "ABC123", selected: true },
        { label: "DEF456" }]},
      titleBar: "inscribed",
      titleBarFontFamily: "monospace",
      maxLabelSizeForTitleBar: Number.MAX_VALUE,
      groupLabelFontFamily: "Arial, sans-serif"
    });

### titleBarFontStyle

Font style for the title bar, if shown. CSS-compliant font style specifications are supported, such as `italic`. If not specified, `groupLabelFontStyle` will be used.

### titleBarFontWeight

Font weight for the title bar, if shown. CSS-compliant font weight specifications are supported, such as `bold`. If not specified, `groupLabelFontWeight` will be used.

### titleBarFontVariant

Font variant for the title bar, if shown. CSS-compliant font variant specifications are supported, such as `small-caps`. If not specified, `groupLabelFontVariant` will be used.

### titleBarMinFontSize

Minimum font size to use when drawing the title bar's label, in pixels.

Note that setting minimum font size to a large value may result in an empty title bar.

### titleBarMaxFontSize

Maximum font size to draw the title bar's label, in pixels.

### titleBarBackgroundColor

The background color of the title bar area.

### titleBarTextColor

The text color for drawing labels in the title bar area.

### titleBarTextPaddingLeftRight

Left and right (horizontal) padding to leave inside the title bar's area.

### titleBarTextPaddingTopBottom

Top and bottom (vertical) padding to leave inside the title bar's area.

### titleBarDecorator

Allows to customize or completely replace the text displayed in the title bar. By default, FoamTree will display the group's label, but you can use this option to display a different text, e.g. some more details related to the group.

The decorator will be called every time the user's pointer hovers over a group. The signature of the decorator callback should be the following:

`function (options, properties, variables)`

where:

options
  all current visualization options (keys and values).
properties

  an object with properties describing the group the user is hovering over. The object will be a union of objects retrieved from the `hierarchy`, `state` and `geometry` options.

  Additionally, the following title-bar-specific properties are available:

  titleBarWidth
    the width of the title bar area, in pixels
  titleBarHeight
    the height of the title bar area, in pixels
  labelFontSize
    the font size used to draw the actual group label or `0` if the label is not drawn
  viewportScale
    Since 3.1.0 the current viewport scale. Values larger than `1` mean the viewport is zoomed-in, values smaller than `1` mean the viewport is zoomed-out, a value of `1` means the viewport is at its neutral zoom level.
variables

  object with title-bar-related variables this function can change.

  The following variables are available:

  titleBarText
    the default text for the title bar, equal to the label of the group. If changed to `null` or `undefined`, the title bar will not be shown.
  titleBarShown
    `true` if the group's label is small enough for the title bar to show, `false` otherwise. You can change the value of this variable to force showing or hiding of the title bar.
  titleBarMaxFontSize
    the maximum font size to be used for drawing text in the title bar, by default equal to the `titleBarMaxFontSize` option. Change this variable to override the the max font size for the currently rendered title bar.

Heads up!

The limited demo version of FoamTree will not call this decorator for the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

The following example shows how to use the title bar decorator to display more details about the group. A similar complete example can be found in titlebar-details.html.

    foamtree.set({
      maxLabelSizeForTitleBar: Number.MAX_VALUE,

      titleBarDecorator: function(options, parameters, variables) {
        variables.titleBarText = "More details about " + parameters.group.label;
      }
    });

The following example shows how to force showing of the title bar for specific groups.

    foamtree.set({
      titleBarDecorator: function(options, parameters, variables) {
        var groupId = parameters.group.id;
        if (groupId === "1" || groupId === "2") {
          variables.titleBarShown = true;
        }
      }
    });

### maxLabelSizeForTitleBar

Maximum group label size in pixels whose label to show in the title bar. Small size of a group may cause its label to be drawn in a small font and thus appear illegible. For this reason, when the user hovers the mouse pointer over a group with a small label, the label can be shown in a dedicated title bar.

The `maxLabelSizeForTitleBar` defines the threshold below which group labels will be shown in the title bar. Setting `maxLabelSizeForTitleBar` to `0` will disable the title bar completely. Setting the option to `Number.MAX_VALUE` will cause the title bar to appear for all groups.

## Attribution

### attributionText

The label of the attribution group. If `attributionText` or `attributionLogo` is not empty, an extra group is added to the visualization with the provided text and logo. If `attributionUrl` is not empty, clicking the attribution group will open the provided URL in the browser.

Heads up!

The limited demo version of FoamTree does not allow to disable or change the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

### attributionLogo

The image to display in the attribution group. The image can be specified as a relative or absolute HTTP URL or a data URI. When using HTTP URLs, the image should to be preloaded before visualization is embedded, otherwise it may not be immediately visible. For this reason, the data URIs are recommended. You can use services like dataurl.net to convert your images to data URIs.

Since version 3.3.1, this option also accepts the `Image` instances. With this enhancement, you can render an SVG attribution logo that will look well regardless of the zoom level.

Heads up!

The limited demo version of FoamTree does not allow to disable or change the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

    var logoImage = new Image();
    logoImage.src = "../demos/assets/img/logo-stub.png";
    logoImage.addEventListener("load", function() {
      // Trigger a reload to show the logo
      foamtree.set("attributionLogo", logoImage.src);
      foamtree.set("dataObject", foamtree.get("dataObject"));
    });

### attributionLogoScale

Determines the scale at which FoamTree should draw the attribution logo. The scale is relative to the bounding box of the group's polygon. A scale of `1.0` means the logo will take the whole available width or height.

Heads up!

The limited demo version of FoamTree does not allow to change the scale of the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

### attributionUrl

The URL to open when the user clicks the attribution group, can be undefined.

Heads up!

The limited demo version of FoamTree does not allow to disable or change the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

### attributionWeight

Sets the weight of the attribution group in relation to the total weight of the root-level groups. You can use this option to influence the size of the attribution group.

Heads up!

The limited demo version of FoamTree does not allow to set the attribution weight to a value smaller than `0.0025`. Please contact Carrot Search for licensing of a fully customizable distribution.

### attributionPosition

Determines the position of the attribution group relative to the visualization container. The following values are supported:

topleft
  top-left corner of the visualization container
topright
  top-right corner of the visualization container
bottomleft
  bottom-left corner of the visualization container
bottomright
  bottom-right corner of the visualization container
top
  top side of the visualization container
bottom
  bottom side of the visualization container
left
  left side of the visualization container
right
  right side of the visualization container
random
  a random point on the perimeter of the visualization container
number in the [0, 360) range
  The angle (in degrees) that determines position on the perimeter of the visualization container, clockwise, starting at the right hand side of the container. For example: `0` is the right-hand side of the container, `45` is the bottom-right corner, `90` is the bottom side of the container etc.

Note: When `layout` is `ordered` or `squarified`, only the `topleft` and `bottomright` attribution group placement is supported. All other values will result in the group being placed in the bottom-right corner of the visualization area.

Note: when there is a small number of groups in the visualization, FoamTree may not be able to place the attribution group exactly at the required point.

The following example places the attribution group in the top-right corner of the visualization.

    foamtree.set("attributionPosition", 360 - 45);
    foamtree.set("dataObject", { groups: randomGroups(25, 1) } );

### attributionDistanceFromCenter

Note: applicable only when `layout` is set to `relaxed`.

Determines the distance of the attribution group from the center of the visualization area. The distance of `0` puts the attribution group in the center of the visualization, the distance of `1` puts the attribution group near the perimeter of the visualization area.

Please note that when there is a small number of groups in the visualization, FoamTree may not be able to place the attribution group exactly at the required point.

The following example attempts to place the attribution group near the center of the visualization area.

    foamtree.set("attributionDistanceFromCenter", 0);
    foamtree.set("dataObject", { groups: randomGroups(25, 1) } );

### attributionTheme

Determines the color of the attribution group. Currently, two values are supported:

light
  Attribution group will be drawn in a light color.
dark
  Attribution group will be drawn in a dark color.

## Rendering

### pixelRatio

The physical-to-display pixel count ratio to assume when drawing the final visualization. On modern devices with high-density screens (such as the Retina display) one logical pixel can be mapped to more than one physical pixel. You can use this option to increase the resolution at which the visualization is drawn to make the image clearer and labels more legible. You can also decrease this resolution to make rendering faster at the cost of quality.

By default, `pixelRatio` is `1`. In such cases, the width and height of the canvas on which the visualization is drawn will be equal to the pixel dimensions of the HTML container element. For `pixelRatio` values greater/smaller than 1, the pixel dimensions of the canvas will be smaller/larger than the dimensions of the enclosing HTML element and the canvas will be stretched to fit in the container's client area. For example, if you set `pixelRatio` to 2 and if the size of the enclosing HTML element is 400x400 pixels, the size of the canvas will be 800x800 pixels. Such a configuration will better utilize the extra pixels available on Retina displays, for example.

On most modern browsers you can retrieve the device-specific pixel ratio from the `window.devicePixelRatio` property.

If the pixel ratio is changed after FoamTree has initialized, you will need to call `redraw` for the change to be applied.

The following examples demonstrate the difference between a pixel ratio higher and lower than the pixel resolution.

    foamtree.set({
      dataObject: largeDataSet,
      pixelRatio: 0.5
    });
    foamtree.redraw();

    foamtree.set({
      dataObject: largeDataSet,
      pixelRatio: 2
    });
    foamtree.redraw();

Heads up!

To boost the performance on iPads with Retina display set `pixelRatio` to 2 and the width of the HTML container element to be at least one pixel less than the total screen width. Yes, we know it's weird but it speeds up rendering a lot.

When setting `pixelRatio` to a value larger than 1 on mobile devices, make sure to correctly set the size of the page's viewport. The default viewport may be very large, leading to a very large canvas. Refer to the mobile demo's source code for examples.

### wireframePixelRatio

The pixel ratio to use when drawing animations, such as rollout, pullback, expose or zooming. You can change the default to a higher value (e.g. `2` on Retina displays) to have the animation drawn in more detail, but noticeably slower. You can also set this option to a lower value, e.g. 0.5 to have the animation drawn faster, but with less detail. Please see the `pixelRatio` option for detailed considerations on how the pixel ratio value affects the sizes of the canvases allocated by FoamTree.

### groupContentDecorator

A callback function you can use to customize or completely replace the way group polygons' content is drawn. The task of the function is to render the contents by invoking the appropriate drawing methods on the provided drawing context that behaves like the standard CanvasRenderingContext2D.

The signature of the decorator callback should be the following:

`function (options, properties, variables)`

where:

options
  all current visualization options (keys and values).
properties

  an object with properties describing the group being rendered. The object will be a union of objects retrieved from the `hierarchy`, `state` and `geometry` options.

  Additionally, a number of shape-decorator-specific properties are available.

variables

  object with a number of variables this decorator can change.

  The following variables are available:

  groupLabelDrawn
    set to `false` to skip rendering of the default group's label; `true` by default.
  groupPolygonDrawn
    set to `false` to skip rendering of the polygon corresponding to the group; `true` by default.

##### Group content decorator specific properties

The `properties` object will contain a number of additional properties useful for drawing custom content in FoamTree polygons.

context

  a drawing context that behaves like the standard CanvasRenderingContext2D. The decorator should call the appropriate drawing methods on this object to render the contents of the polygon.

  The provided object is not an actual canvas drawing context, but a buffer that records all the invocations so that FoamTree can replay them when needed without invoking the decorator, for example during zooming or panning. The `groupContentDecoratorTriggering` option determines when this decorator will be triggered and when the last buffered content will be "replayed" without invoking the decorator.

  The drawing context buffer offers a number of additional methods not available in a standard drawing context, such as drawing rounded rectangles or filling a polygon with text.

shapeDirty

  indicates whether the geometry of the group's polygon has changed since the last invocation of the decorator. If the custom layout is expensive to compute, you may want to cache it and recompute the layout only when the `shapeDirty` property is `true`.

  This property is meaningful only when `groupContentDecoratorTriggering` is `onSurfaceDirty`. If the triggering is done `onShapeDirty`, the `shapeDirty` property will `true` on all invocations of the decorator.

viewportScale

  the current viewport scale. Values larger than `1` mean the viewport is zoomed-in, values smaller than `1` mean the viewport is zoomed-out, a value of `1` means the viewport is at its neutral zoom level.

  Using this property makes sense only when `groupContentDecoratorTriggering` is `onSurfaceDirty`, in which case you may want to vary the amount of detail drawn depending on the zoom level.

polygonContext

  Since 3.4.0 a read-only context buffer containing the drawing commands that set the path corresponding to the group's polygon. You can `replay()` this buffer to the drawing context, so that later you can `fill()`, `stroke()` or `clip()` the polygon's shape. In most cases you'll set the `groupPolygonDrawn` variable to `false` to prevent FoamTree from drawing the default polygon.

  Use cases of this buffer range from drawing custom fill or stroke around the group polygon to filling the polygon with a pattern or an image. In tandem with the `labelContext` property, you can use it to alter the default order of drawing group elements. See the Photo explorer or the SCADA dashboard demos for real-world applications.

  The following example uses the `polygonContext` property to draw custom gradient fills based on a 0..1 value associated with each group.

      foamtree.set({
        dataObject: { groups: randomGroups(30) },
        onModelChanging: function (dataObject) {
          dataObject.groups.forEach(function (group) {
            group.value = 0.2 + Math.random() * 0.8
          })
        },
        groupContentDecorator: function(opts, props, vars) {
          var group = props.group;
          var value = group.value;
          if (!value) {
            return;
          }

          // Compute the geometry of the gradient
          var boundingBox = CarrotSearchFoamTree
              .geometry.boundingBox(props.polygon);
          var ctx = props.context;
          var gradient = ctx.createLinearGradient(
              boundingBox.x, boundingBox.y + boundingBox.h,
              boundingBox.x, boundingBox.y);

          // Compute base color based on the value
          var hue = (-10 + 110.0 * (1 - 2 * Math.abs(value - 0.5)));
          var baseColor = "hsla(" + hue.toFixed(1) + ", "
              + (90 + (props.hovered ? 10 : 0)) + "%, "
              + (40 + (props.hovered ? 20 : 0)) + "%, 1.0)";

          // Set up color steps
          var th = 0.05;
          gradient.addColorStop(0, baseColor);
          if (value > th) {
            gradient.addColorStop(value - th, baseColor);
          }
          gradient.addColorStop(value, "rgba(255, 255, 255, 0.5)");
          if (value + th < 1) {
            gradient.addColorStop(value + th, "rgba(255, 255, 255, 0.5)");
          }

          // Prevent default drawing of the polygon
          vars.groupPolygonDrawn = false;

          // Set up polygon path
          ctx.beginPath();
          props.polygonContext.replay(ctx);
          ctx.closePath();

          // Fill with custom gradient
          ctx.fillStyle = gradient;
          ctx.fill();

          // Stroke
          ctx.strokeStyle = "hsla(" + hue.toFixed(1) + ", "
              + (90 + (props.hovered ? 10 : 0)) + "%, "
              + (30 + (props.hovered ? 20 : 0)) + "%, 1.0)";
          ctx.stroke();
        }
      });

labelContext

  Since 3.4.0 a read-only context buffer containing the commands that draw the default group label. You can `replay()` this buffer to the drawing context to actually draw the label.

  Use cases of this buffer range from drawing the label with custom fill or transformation to altering the default order of drawing group elements.

  The following example uses the `labelContext` property to draw slightly rotated default labels. Please note that label fitting is still performed in the non-rotated setting, so the rotation may cause some labels to stick outside of their polygons.

      foamtree.set({
        dataObject: largeDataSet,
        groupContentDecorator: function(opts, props, vars) {
          vars.groupLabelDrawn = false;
          var ctx = props.context;
          ctx.translate(props.polygonCenterX, props.polygonCenterY);
          ctx.rotate(-Math.PI / 16);
          ctx.translate(-props.polygonCenterX, -props.polygonCenterY);
          props.labelContext.replay(ctx);
        }
      });

##### Tips & caveats

Please note the following tips and caveats when implementing your drawing code:

- The coordinate space in which all drawing happens is determined by the coordinates of the group's polygon. The coordinates of the polygon are available in the `polygon` property. You may want to apply a suitable transformation to the drawing context to avoid complex coordinate arithmetics.
- To make the custom content interactive, you can use the `onGroupMouseMove` event along with the visualization-relative coordinates. These coordinates correspond to the ones you'll use to draw the custom content. See the Interactive custom content demo for some example code.
- FoamTree exposes a number of geometry utilities that may be useful when implementing the shape decorator code.

- FoamTree does not restrict the drawing area to the area of the polygon being filled. This means that it is the responsibility of your code not to overrun the polygon boundaries. If your code draws beyond the boundaries of the polygon, you will be seeing artifacts resulting from incremental redraws and arbitrary order of polygon drawing.

  One option is to use the `clip` method of the drawing context to restrict the drawing area, but this will severely impact the drawing performance. A much faster variant would be, for example, to restrict drawing to an inscribed rectangle computed using the `rectangleInPolygon` method.

- Due to performance optimizations applied by FoamTree, such as incremental redraws, your code must not make any assumptions about the order in which this decorator will be invoked. If the code needs to precompute any global information, the best place to do so would be the `onRedraw` or `onModelChanged` event listeners. See also the `groupContentDecoratorTriggering` option.

- Each group's elements are drawn in the following order:

  1. group's polygon (unless the decorator sets the `groupPolygonDrawn` variable to `false`)
  2. the shapes drawn by the decorator
  3. the default group's label (unless the decorator sets the `groupLabelDrawn` variable to `false`)
- If the group has a non-empty label, the custom content will only be drawn when the label is visible. When the current viewport scale is too small for the label to be drawn, the custom content will not be drawn and the label-too-small dots will appear instead. If, however, the group does not have a label, custom content will always be drawn. In this case, this decorator may further decide whether to draw any content depending on the size of the polygon and the current viewport scale.

Heads up!

The limited demo version of FoamTree will not call this decorator for the attribution group. Please contact Carrot Search for licensing of a fully customizable distribution.

##### Code examples

The following example shows how to use the group shape decorator to draw a two-way pie chart that can express some additional information about the group.

    foamtree.set({
      rolloutDuration: 3000,
      dataObject: largeDataSet,

      // Our custom drawing depends on the group state,
      // therefore we need to use the "onSurfaceDirty" triggering.
      groupContentDecoratorTriggering: "onSurfaceDirty",

      // Our custom drawing is pretty fast, so we can draw it during animations
      wireframeContentDecorationDrawing: "always",

      // Our custom drawing callback
      groupContentDecorator: function (opts, params, vars) {
        var ctx = params.context;
        var centerX = params.polygonCenterX;
        var centerY = params.polygonCenterY;

        // Compute pie chart radius
        var radius = CarrotSearchFoamTree.geometry.circleInPolygon(
          params.polygon, centerX, centerY) * 0.8;

        // Increase opacity of the pie chart on hover
        var baseAlpha = (params.hovered ? 2 : 1);

        // Increase stroke width on selection,
        // use thinner lines on lower hierarchy levels
        ctx.lineWidth = (params.selected ? 3.0 : 1.5) * Math.pow(0.5, params.level);

        // The black part of the pie chart
        ctx.beginPath();
        ctx.moveTo(centerX, centerY);
        ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI * params.group.rank);
        ctx.closePath();
        ctx.strokeStyle = rgba("0, 0, 0", baseAlpha * 0.2);
        ctx.stroke();
        ctx.fillStyle = rgba("0, 0, 0", baseAlpha * 0.1);
        ctx.fill();

        // The white part of the pie chart
        ctx.beginPath();
        ctx.arc(centerX, centerY, radius, 2 * Math.PI * params.group.rank, 0);
        ctx.strokeStyle = rgba("255, 255, 255", baseAlpha * 0.2);
        ctx.stroke();
        ctx.lineTo(centerX, centerY);
        ctx.fillStyle = rgba("255, 255, 255", baseAlpha * 0.2);
        ctx.fill();

        function rgba(rgb, alpha) {
          return "rgba(" + rgb + ", " + alpha + ")";
        }
      },

      // Generates some random data we'll display in the pie chart
      onModelChanged: function (data) {
        enhance(data);

        function enhance(group) {
          group.rank = 0.1 + Math.random() * 0.9;
          if (group.groups) {
            group.groups.forEach(enhance);
          }
        }
      }
    });

The following example shows how to draw some extra information below the default label.

    foamtree.set({
      dataObject: largeDataSet,

      // Our custom drawing callback
      groupContentDecorator: function (opts, params, vars) {
        var ctx = params.context;
        var centerX = params.polygonCenterX;

        // We want to put the extra label inside a semi transparent rectangle.
        // To do this, we need to draw the rectangle first and then the label.
        // The dimensions of the rectangle are returned by the fillPolygonWithText()
        // method, so to get the right drawing order, we need to draw the text
        // to a scratch buffer, draw the rectangle and then draw the text we
        // saved in the scratch buffer.
        var scratch = ctx.scratch();

        var info = scratch.fillPolygonWithText(
          // Fit the extra text inside the main polygon
          params.polygon,

          // Fit the extra text below the main label
          centerX, params.labelBoxTop + params.labelBoxHeight,

          // use non-breakable space to prevent line breaks
          params.group.weight + "\u00a0docs",

          {
            maxFontSize: 0.6 * params.labelFontSize, // restrict max font size
            verticalAlign: "top", // flow the text downwards from the center point
            verticalPadding: 0.1, // use some smaller than default padding
            maxTotalHeight: 1 // use the full available height
          });

        // Draw the rectangle. FoamTree already set for us the color
        // it used to draw the label, we'll use that color.
        if (info.fit) {
          var labelBox = info.box;
          var boxMargin = labelBox.h * 0.1;
          ctx.roundRect(labelBox.x - 2 * boxMargin, labelBox.y - boxMargin,
            labelBox.w + 4 * boxMargin, labelBox.h + 2 * boxMargin, boxMargin * 2);

          ctx.globalAlpha = 0.15;
          ctx.fill();
          ctx.globalAlpha = 0.25;
          ctx.lineWidth = boxMargin * 0.3;
          ctx.stroke();

          // Draw the text from our scratch buffer
          ctx.globalAlpha = 1.0;
          scratch.replay(ctx);
        }
      }
    });

See also the images demo for code that renders SVG images inside FoamTree polygons and the GitHub search demo for more complex custom content rendering.

### groupContentDecoratorTriggering

Determines when the `groupContentDecorator` will be triggered:

onShapeDirty

  The shape decorator will only be triggered if the shape of the group's polygon changes, for example during the relaxation process or after resizing of the visualization area. During redraws in which the shape of the polygon is not changing, such as when zooming or drawing selection outline, FoamTree will "replay" the custom drawing commands issued by the last invocation of the shape decorator, which will improve the rendering performance.

  Use this triggering mode when the custom content of the group does not depend on the state of the group (selected, hovered) and thus the group shape decorator does not need to be called when group state changes.

onSurfaceDirty

  The shape decorator will be triggered every time the group's polygon needs to be drawn. Use this triggering mode if your custom content drawing depends on the state of the group (selected, hovered). Depending on the complexity of your custom drawing code, using this triggering mode may result in lower rendering performance.

### wireframeLabelDrawing

Determines whether FoamTree should draw group labels during animation. The following values are allowed:

auto
  FoamTree will try to automatically enable or disable drawing of labels based on the performance of the browser and the size of the data set to meet the limit set in `wireframeDrawMaxDuration`.
always
  labels will always be drawn during animations, regardless of the `wireframeDrawMaxDuration` limit.
never
  labels will never be drawn during animations, regardless of the `wireframeDrawMaxDuration` limit.

### wireframeContentDecorationDrawing

Determines whether FoamTree should draw the group content decorations produced by the `groupContentDecorator` during animation. The following values are allowed:

auto

  FoamTree will try to automatically enable or disable drawing of group content decorations based on the performance of the browser and the size of the data set to meet the limit set in `wireframeDrawMaxDuration`.

  For the purposes of this calculation FoamTree will assume that the custom shapes drawn by the `groupContentDecorator` are as expensive to draw as labels. If the custom drawing does not contain any text and/or is fast to draw, you can set this option to `always`.

always
  group content decorations will always be drawn during animations, regardless of the `wireframeDrawMaxDuration` limit.
never
  group content decorations will never be drawn during animations, regardless of the `wireframeDrawMaxDuration` limit.

### groupLabelLayoutDecorator

A callback function you can use to customize how the label of a specific group is laid out, including the font, size and paddings.

The signature of the decorator callback should be the following:

`function (options, properties, variables)`

where:

options
  all current visualization options (keys and values).
properties

  an object with properties describing the group being rendered. The object will be a union of objects retrieved from the `hierarchy`, `state` and `geometry` options.

variables

  object with a number of variables this decorator can change. The following variables are available:

  fontFamily
    See fontFamily in fillPolygonWithText().
  fontStyle
    See fontFamily in fillPolygonWithText().
  fontVariant
    See fontVariant in fillPolygonWithText().
  fontWeight
    See fontWeight in fillPolygonWithText().
  lineHeight
    See lineHeight in fillPolygonWithText().
  horizontalPadding
    See horizontalPadding in fillPolygonWithText().
  verticalPadding
    See verticalPadding in fillPolygonWithText().
  maxTotalTextHeight
    See fontFamily in fillPolygonWithText().
  maxFontSize
    See maxFontSize in fillPolygonWithText().

One use case for this callback is lowering label padding for description groups for flattened `stacking` as demonstrated by the following example.

    foamtree.set({
      dataObject: largeDataSet,
      stacking: "flattened",
      groupLabelLayoutDecorator: function (opts, props, vars) {
        if (props.description) {
          // Lower padding
          vars.verticalPadding = 0.1;

          // Allow the label to take the full
          // height of the description group
          vars.maxTotalTextHeight = 1.0;

          // Render description labels in bold
          vars.fontWeight = "700";
        }
      }
    });

### descriptionGroupPolygonDrawn

Note: applicable only when `stacking` is set to `flattened`.

When `true`, the polygon corresponding to the description group will be drawn. By default, the polygon is not drawn to make an impression that the description group is linked to the parent polygon.

### maxGroupLevelsDrawn

The maximum number of levels of closed groups FoamTree will draw. For hierarchies with more than `maxGroupLevelsDrawn` levels, the lower-level groups will not be drawn until the appropriate number of their parent groups get open. If your data set contains many levels of groups with many children on each level, you may want to lower the value of this option to speed up layout computation and rendering at the cost of lower amount of visible detail.

The following example generates a random 10-level hierarchy with 2 groups at each level, sets the initial `maxGroupLevelsDrawn` to `1` so that only the top-level groups are drawn. Then, once a second, the code will increment `maxGroupLevelsDrawn` to demonstrate that lower-level groups will indeed be drawn. Additionally, the code will gradually lower the `parentFillOpacity`, so that the lower-level groups are more clearly visible. Finally, the example sets `groupMinDiameter` to `0` so that as many groups are possible fit on the screen.

    foamtree.set({
      // Lower groupMinDiameter to fit as many groups as possible
      groupMinDiameter: 0,

      // Start with drawing just the top-level groups
      maxGroupLevelsDrawn: 1,

      // Eagerly compute the layout for all levels
      maxGroupLevelsAttached: 10,

      // Generate a 10-level hierarchy
      dataObject: { groups: randomGroups(2, 10 /* levels */) }
    });

    // Initiate revealing of lower levels
    var timeout;
    (function reveal() {
      timeout = window.setTimeout(function () {
        var levels = foamtree.get("maxGroupLevelsDrawn") + 1;
        foamtree.set("maxGroupLevelsDrawn", levels);
        foamtree.set("parentFillOpacity", foamtree.get("parentFillOpacity") * 0.8);
        foamtree.redraw();
        if (levels < 10) {
          reveal();
        }
      }, 1000);
    })();

### maxGroupLabelLevelsDrawn

The maximum number of levels of group labels FoamTree will draw. For hierarchies with more than `maxGroupLabelLevelsDrawn` levels, only the top specified number of open groups will have their labels drawn. You can lower the default value of this option for faster rendering of the visualization at the cost of lower amount of visible detail.

The following example generates and visualizes a random 10-level hierarchy with 2 groups at each level. Click and hold on the groups to open them and observe how deeper level polygons and labels get revealed.

    foamtree.set({
      // Lower groupMinDiameter to fit as many groups as possible
      groupMinDiameter: 0,

      // Draw 2 levels of labels and 4 levels of group polygons
      maxGroupLevelsDrawn: 4,
      maxGroupLabelLevelsDrawn: 2,

      // Tune the border options to make them more visible
      parentFillOpacity: 0.3,
      parentStrokeOpacity: 1.0,
      groupBorderRadius: 0,
      groupBorderWidth: 3,
      groupBorderWidthScaling: 0.8,

      // Generate a 10-level hierarchy
      dataObject: { groups: randomGroups(2, 10 /* levels */) },

      // Always draw the same "label"
      groupLabelDecorator: function (opts, params, vars) {
        vars.labelText = params.level;
      }
    });

### maxGroupLevelsAttached

The maximum number of group levels for which to compute layout when new `dataObject` is set. Combined with the `attach` method, this option can be used to improve the responsiveness of FoamTree when visualizing deeply-nested hierarchies with large numbers of groups on the top level.

For more details and examples, see the `attach` method and the Deferred layout of child groups demo.

### incrementalDraw

Determines how FoamTree decides whether to apply incremental visualization redraws. The following values are supported:

fast

  FoamTree will try to redraw as few groups as possible when an incremental update is possible. With typical settings, this procedure will result in quick updates and no visual artifacts.

  In case of non-typical settings, such as when `groupSelectionOutlineWidth` is larger than half the `groupBorderWidth`, you may occasionally see visual artifacts when using fast incremental updates. If this is the case, consider setting `incrementalDraw` to `accurate`.

accurate
  FoamTree will apply a more conservative procedure when deciding whether an incremental draw can be performed. In particular, if `groupSelectionOutlineWidth` is larger than half the `groupBorderWidth`, FoamTree will always redraw all groups.
none
  FoamTree will always redraw all groups, even if the settings permit incremental redraws.

### wireframeToFinalFadeDelay

Once animation of the visualization completes, FoamTree will redraw the final image using more detail. This option determines the amount of time that should pass after the last animation frame before the high-quality image is shown.

### wireframeToFinalFadeDuration

The duration of the fading animation used to switch between the wireframe and the high-quality visualization image.

### finalToWireframeFadeDuration

The duration of the fading animation used to switch between the high-quality and the wireframe visualization image.

### wireframeDrawMaxDuration

The desired maximum duration of drawing one frame of animation. Decreasing the value, possibly to `0`, will make the animation more smooth at the cost of lower number of details. Increasing the value will increase the number of details, but may slow down the animation.

### finalCompleteDrawMaxDuration

The desired maximum duration of a complete high-quality redraw of the visualization. Please see the Visualization rendering performance section for a detailed discussion.

### finalIncrementalDrawMaxDuration

The desired maximum duration of an incremental high-quality redraw of the visualization. Please see the Visualization rendering performance section for a detailed discussion.

### androidStockBrowserWorkaround

There is a bug in Android stock browsers, that affects the incremental updates of the visualization as well as cross-fading between the wireframe and final visualization images. Setting this option to `true` will enable a workaround for the bug. As a side effect, cross fading between the wireframe and final images will be disabled, regardless of the `wireframeToFinalFadeDuration` amd `finalToWireframeFadeDuration` options.

The default value of this option is `true` on all Android browsers and `false` on all other browsers.

### viewport

A read-only option that returns the parameters of the current viewport. This option returns an object with the following properties:

x
  The horizontal offset of the viewport.
y
  The vertical offset of the viewport.
scale
  The scale of the viewport. Values larger than `1.0` mean the viewport is magnified. A value of `1.0` means the viewport is in its neutral state. Values lower than `1.0` mean the viewport is demagnified, which may happen when some groups are exposed.

The following example renders the current viewport parameters inside each group's polygon.

    foamtree.set({
      dataObject: { groups: randomGroups(10) },

      groupLabelDecorator: function (opts, params, vars) {
        var viewport = this.get("viewport");
        vars.labelText =
          "x\u00a0=\u00a0" + viewport.x.toFixed(2) + " " +
          "y\u00a0=\u00a0" + viewport.y.toFixed(2) + " " +
          "scale\u00a0=\u00a0" + viewport.scale.toFixed(2);
      }
    });

The example below shows how to prevent panning of the visualization area if the viewport is not zoomed-in.

    foamtree.set({
      dataObject: { groups: randomGroups(10) },

      onGroupDragStart: function (e) {
        if (this.get("viewport").scale <= 1.0) {
          return false;
        }
      }
    });

## Export

### imageData

Returns the current state of the visualization as an image in the data URL format. Unlike most attributes, this one accepts an optional object which can specify the image format details. This object can contain the following keys:

format
  format of the image data: `image/png` or `image/jpeg`
quality
  if `format` is `image/jpeg`, specifies the desired quality of JPEG compression in the 0..1 range, where 1 means the highest quality and largest image data. Note that JPEG images are always opaque, even if the background color is specified as transparent. Use PNG images to handle background transparency.
pixelRatio
  the pixel ratio to use when producing the export image. Use a value larger than `1`, such as `2` to create a higher-resolution image.
backgroundColor
  if specified, the background of the exported image will be filled in with the provided color. This option is especially useful when exporting images in the JPEG format.

The visualization image can be used by your application in many different ways. On certain browsers, it is possible to trigger a dialog allowing the user to save the image to the local disk. The image data can be also sent to a server that will do further processing (for example save it or send it via e-mail somewhere).

In the following example, we open up a new browser window (or tab) and populate the `img` element with the data URI acquired from the current state of foamtree:

    foamtree.set({
      dataObject: { groups: [
        { label: "Click me to get the image", trigger: true, weight: 5 },
        { label: "Group 1" },
        { label: "Group 2" },
        { label: "Group 3" },
        { label: "Group 3" }
      ]},
      onGroupClick: function (event) {
        if (event.group.trigger) {
          event.preventDefault();

          let htmlContent = "<html><body><img width='100%' src='$data' />world</body></html>";
          htmlContent = htmlContent.replace("$data",
            foamtree.get("imageData", {
              format: "image/png"
            }));

          const newWindow = window.open();
          newWindow.document.write(htmlContent);
          newWindow.document.close();
        }
      }
    });

Heads up!

If the attribution logo was fetched from a different domain than the page that embeds the visualization, getting the visualization image will not be possible due to security constraints.

Opening up the image using top-level data URIs (new window) is blocked on most browsers due to security considerations: see "top-level navigation blocked" section of this chart.

The following example logs (to the browser's `console`) the size of data URL string for the same visualization image compressed using varying settings (the data URL string size is logged, not actual image bytes).

    foamtree.set({
      dataObject: largeDataSet,
      onRolloutComplete: function() {
        var opts = [
          { format: "image/png" },
          { format: "image/jpeg", quality: 0.2 },
          { format: "image/jpeg", quality: 0.4 },
          { format: "image/jpeg", quality: 0.6 },
          { format: "image/jpeg", quality: 0.8 },
          { format: "image/jpeg", quality: 1 }
        ];
        $(opts).each(function(i, format) {
          console.log(format, "Image takes: " +
                      (foamtree.get("imageData", format).length / 1024).toFixed(0) + "kB");
        });
      }
    });

## Interactions

### selection

You can use this option to obtain or alter the current selection.

getter

  Calling `get("selection")` returns an object with the currently selected groups. The object contains one property, `groups`, containing an array of references to the data objects corresponding to the selected groups.

      foamtree.set({
        groupSelectionOutlineColor: "red",
        groupSelectionOutlineWidth: 6,
        groupSelectionOutlineShadowSize: 4
      })

      foamtree.set("dataObject", { groups: [
        { label: "Group 1", selected: true },
        { label: "Group 2" },
        { label: "Group 3", selected: true },
        { label: "Group 4" }]});

      console.log(foamtree.get("selection").groups);

setter

  You can set the selection option to alter the current selection. The provided value must be a multiple group selector designating the groups to select or deselect.

  - If the selector is an individual selector or an array of individual selectors, the designated groups will get selected, the state of the other groups will remain unchanged.

        foamtree.set("selection", "1");
        var timeout = window.setTimeout(function() {
          foamtree.set("selection", "2");
        }, 2000);

  - If the selector is an object, two additional properties are supported, apart from the common `groups` and `all` properties:

    selected
      If `true` or not provided, the designated groups will get selected. If `false`, the designated groups will get deselected.
    keepPrevious
      If `true` or not provided, only the state of the designated groups will be altered. If `false`, the state of the designated groups will be altered, the other groups will get closed.

    To deselect all groups, you can use the selector with the `all` property:

        foamtree.set("selection", ["1", "2"]);
        var timeout = window.setTimeout(function() {
          foamtree.set("selection", { all: true, selected: false });
        }, 2000);

  Note: when selection state is changed by setting this option, the event listeners will not be called.

### open

You can use this option to obtain or alter the set of open groups.

getter

  Calling `get("open")` returns an object with the currently open groups. The object contains one property, `groups`, containing an array of references to the data objects corresponding to the open groups.

      foamtree.set("dataObject", { groups: [
        { label: "Group 1", groups: [ { label: "Group 1.1" }, {label: "Group 1.2" } ], open: true },
        { label: "Group 2" },
        { label: "Group 3", groups: [ { label: "Group 3.1" }, {label: "Group 3.2" } ], open: true },
        { label: "Group 4" }]});

      console.log(foamtree.get("open").groups);

setter

  You can set this option to alter the currently open groups. The provided value must be a multiple group selector designating the groups to open or close.

  - If the selector is an individual selector or an array of individual selectors, the designated groups will get open, the state of the other groups will remain unchanged.

        foamtree.set("open", "1");
        var timeout = window.setTimeout(function() {
          foamtree.set("open", "2");
        }, 2000);

  - If the selector is an object, two additional properties are supported, apart from the common `groups` and `all` properties:

    open
      If `true` or not provided, the designated groups will get open. If `false`, the designated groups will get closed.
    keepPrevious
      If `true` or not provided, only the state of the designated groups will be altered. If `false`, the state of the designated groups will be altered, the other groups will get closed.

    To close all groups, you can use the selector with the `all` property:

        foamtree.set("open", ["1", "2"]);
        var timeout = window.setTimeout(function() {
          foamtree.set("open", { all: true, open: false });
        }, 2000);

  Note: when open state is changed by setting this option, the event listeners will not be called.

  Tip: to get a notification of the completed open/close animation, you can use the `open` method instead.

### exposure

You can use this option to obtain or alter the set of exposed groups.

getter

  Calling `get("exposure")` returns an object with the currently exposed groups. The object contains one property, `groups`, containing an array of references to the data objects corresponding to the exposed groups.

      foamtree.set("dataObject", { groups: [
        { label: "Group 1", exposed: true },
        { label: "Group 2" },
        { label: "Group 3", exposed: true },
        { label: "Group 4" }]});

      console.log(foamtree.get("exposure").groups);

setter

  You can set this option to alter the currently open groups. The provided value must be a multiple group selector designating the groups to expose or unexpose.

  - If the selector is an individual selector or an array of individual selectors, the designated groups will be exposed, all other groups will get unexposed.

        foamtree.set("exposure", "1");
        var timeout = window.setTimeout(function() {
          foamtree.set("exposure", "2");
        }, 2000);

    The following example shows how to unexpose all groups.

        foamtree.set("exposure", ["1", "2"]);
        var timeout = window.setTimeout(function() {
          foamtree.set("exposure", []);
        }, 2000);

    A similar effect could be achieved using the `reset` method.

  - If the selector is an object, two additional properties are supported, apart from the common `groups` and `all` properties:

    exposed
      If `true` or not provided, the designated groups will get exposed. If `false`, the designated groups will be unexposed.
    keepPrevious
      If `true`, only the exposure of the designated groups will be altered. If `false` or not provided, the exposure of the designated groups will be altered, the other groups will get unexposed.

    The following example uses the `keepExpose` property of the group selector to expose one more group in addition to the already exposed ones. Notice that if the parent group of the group to be exposed is closed (groups 3 and 4 in the example), the parents will be automatically opened as part of the exposure process.

        foamtree.set("exposure", ["1", "2"]);
        var timeout = window.setTimeout(function() {
          foamtree.set("exposure", { groups: ["3.1", "4.1"], keepPrevious: true });
        }, 2000);

  Note: when exposure is changed by setting this option, the event listeners will not be called.

  Note: there are some limitations when relaxation is visible.

  Tip: to get a notification of the completed expose animation, you can use the `expose` method instead.

### state

Returns information about the state of the group. The returned object contains the following properties:

selected
  `true` if the group is currently selected
hovered
  `true` if the group is currently hovered over
open
  `true` if the group is currently open
openness
  the progress of the opening/closing animation on the `0..1` scale
exposed
  `true` if the group is currently exposed
exposure
  the progress of the expose animation on the `0..1` scale
transitionProgress
  the progress of the rollout/pullback animation on the `0..1` scale
revealed
  `true` if the group is being drawn on the screen, also when the group is currently invisible due to zooming/panning; `false` when the group is not being drawn on the screen, due to, for example, too small size or because the relaxation process has not yet revealed it.
browseable
  Since 3.2.0 This property can assume three states:
  `true`
    FoamTree has computed the layout for the direct children of this group and the group can be opened for browsing.
  `false`

    FoamTree has attempted to compute the layout for the direct children of this group, but the layout could not be computed for any of the following reasons: a. the diameter of the child groups would not exceed `groupMinDiameter`, b. the total limit of `maxGroups` would have been exceeded if the layouts got computed, c. JavaScript standard floating point precision is not enough to compute the layout, which can happen for very deeply nested groups.

    When the group is not browseable, you may want to allow the user to reload the visualization passing the non-browseable group as the `dataObject`, and possibly allow the user to get back to the original top-level data.

  `undefined`
    FoamTree has not yet attempted to compute the layout for the direct children of this group. FoamTree will attempt to compute the layout when at most `maxGroupLevelsDrawn` above this groups are open.
visible
  `true` if the group is visible in the current viewport
labelDrawn
  `true` if the group's label is being drawn; `false` if the label is not being drawn due to, for example, to small area of the polygon.

To retrieve the information about a group, provide the individual group selector as the second parameter of the `get` method:

    var timeout = window.setTimeout(function() {
      console.log(foamtree.get("state", "1"));
    }, 500);

### interactionHandler

Determines which use interaction capture mechanism FoamTree should be using:

builtin
  the built-in mechanism, suitable mostly for desktop devices
hammerjs
  use Hammer.js to capture the interactions, see the Touch devices section for details.
external
  all interaction events will be triggered externally, please see the Externally triggered interaction section for details.

The default value of this option is `builtin` on desktop browsers and `hammerjs` on touch-enabled devices.

## Debugging

### logging

Enables logging of some debug information to `console`.

### times

Execution times and statistics. The details of the returned object are not guaranteed to work between versions but it may turn useful for debugging performance problems.

    window.setTimeout(function() {
      console.log("Execution time stats: ", foamtree.get("times"));
    }, 500);

# Events

## Visualization events

This section describes events that relate to the life cycle of the whole visualization.

### onModelChanging

Called after the pullback animation of the previous data model has completed, but before the visualization has parsed the new data model provided in the `dataObject` option and before the new data has been rendered. The listeners will be called with one argument – the new data object the visualization is about to parse and show.

You can use this event to modify and enhance the data model. For example, you can add or remove groups and sub-groups at this stage, as in the following example:

    foamtree.set({
      onModelChanging: function (data) {
        data.groups.forEach(function (group) {
          group.label = "Group with " + group.count + " child groups";
          group.weight = group.count;

          // Generate the child groups
          group.groups = [];
          for (var i = 0; i < group.count; i++) {
            group.groups.push({ label: "Group " + i });
          }
        });
      }
    });

    foamtree.set("dataObject", { groups: [
      { count: 8  },
      { count: 12 },
      { count: 5  },
      { count: 4  },
      { count: 7  }
    ]});

In the context of each callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onModelChanged

Called after the pullback animation of the previous data model has completed and the visualization has parsed the new data model provided in the `dataObject` option, but before the new data has been rendered. The listeners will be called with one argument – the new data object the visualization is about to show.

You can use this event to enhance the data model. For example, if the computation of the group's polygon color is expensive, you may want to perform the computation once in the `onModelChanged` listener, store it in the data object and then use it in the `groupColorDecorator`, as shown in the following example.

    foamtree.set({
      onModelChanged: function (data) {
        data.groups.forEach(function (group) {
          group.color = "hsl(" + (-60 * (group.sentiment - 1)) + ", 100%, 50%)";
        });
      },
      groupColorDecorator: function (opts, props, vars) {
        vars.groupColor = props.group.color;
      }
    });

    foamtree.set("dataObject", { groups: [
      { label: "Positive", sentiment: 1 },
      { label: "Negative", sentiment: -1 },
      { label: "Neutral",  sentiment: 0 }
    ]});

Note: group hierarchy changes, such as adding or removing groups or sub-groups, will not be taken into account in the `onModelChanged`. You can make such changes in the `onModelChanging` listener.

In the context of each callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onRolloutStart

Called just before the visualization starts the animated rollout. If the application implements some sort of loading indicator, the indicator should be hidden once this event is fired.

Heads up!

This listener will not be called when an empty (`null`, `undefined`) `dataObject` is set. To get a notification in such cases, register a listener for the `onModelChanging` or the `onModelChanged` event.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onRolloutComplete

Called after the rollout animation has completed. Please note that this listener will not be called when an empty `dataObject` is set, see `onRolloutStart` for an alternative for such cases.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onRelaxationStep

Invoked once per layout relaxation step, but only when `relaxationVisible` is `true`. The signature of the listener function should be the following:

`function (relaxationProgress, relaxationComplete, relaxationTimeout)`

where:

relaxationProgress
  the progress of the relaxation process on the 0..1 scale
relaxationComplete
  When `true`, this is the last step of relaxation because the requested `relaxationQualityThreshold` has been achieved.
relaxationTimeout
  When `true`, this is the last step of relaxation because the `relaxationMaxDuration` has been exceeded.

You can use this event to create a progress indicator for the relaxation, just as the relaxation progress utility does.

### onRedraw

Called after the internal- or API-triggered redraw of the visualization. The listener will be called with one boolean parameter set to `true` if the redraw as an incremental one.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

Any code in this callback should be minimal and ultra fast to keep the transition animations smooth.

The following example uses the `onRedraw` listener to count incremental and complete redraws. The data object contains two special groups that hold the counters. The example also defines a simple `groupColorDecorator` that draws the complete redraws group in red and the incremental redraws group in yellow.

    foamtree.set({
      onRedraw: function (incremental) {
        var group = this.get("dataObject").groups[incremental ? 0 : 1];
        group.redraws++;
        group.label = group.redraws.toString();
      },
      groupColorDecorator: function(opts, props, vars) {
        switch (props.group.type) {
          case "complete":    vars.groupColor = "hsl(0, 65%, 75%)"; break;
          case "incremental": vars.groupColor = "hsl(60, 65%, 75%)"; break;
          default:            vars.groupColor = "hsl(0, 0%, 85%)";
        }
      },
      rolloutDuration: 1000
    });

    foamtree.set("dataObject", { groups: [
      { label: "0", redraws: 0, weight: 2, type: "incremental" },
      { label: "0", redraws: 0, weight: 2, type: "complete" },
      { label: "" }, { label: "" },
      { label: "" }, { label: "" },
      { label: "" }, { label: "" },
      { label: "" }, { label: "" }
    ]});

### onViewResetting

Invoked when FoamTree is about to reset the view in response to the view reset interaction.

This callback receives one argument: the event object containing the `preventDefault` property. If you call the `preventDefault()` function, FoamTree will not close and unexpose groups as part of the view reset operation.

See the `reset` method for more details.

### onViewReset

Invoked after the view has been reset by a user interaction. See the `reset` method for more details.

## Group state events

This section documents events triggered when the state of one or more groups is changed by the user's interaction.

### onGroupSelectionChanging

Called during group selection changes. For each selected or deselected group, the listener will be invoked with one parameter containing the following properties:

group
  Reference to the data object which is subject to selection state change.
selected
  `true` if the group has just been selected, `false` otherwise

Note that one user action may result in many events of this type, for example deselection of currently selected groups and selection of another group. The global selection state is easier to track using `onGroupSelectionChanged` event.

When multiple callbacks are present the entire, set will be invoked, even if one or more callbacks changes the selection via an API call.

The listener will not be invoked for API-initiated state changes, only for user interactions. In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupSelectionChanged

Called once after the selection has changed. The callback receives one parameter: an object containing the `groups` property with an array of references to the data objects representing the groups that are currently selected.

When multiple callbacks are present, the entire set will be invoked, even if one or more callbacks changes the selection via an API call.

The listener will not be invoked for API-initiated state changes, only for user interactions. In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

    foamtree.set({
      onGroupSelectionChanged: function(info) {
        console.log(info);
      }
    });

### onGroupExposureChanging

Called during group exposure changes. For each exposed or unexposed group, the listener will be invoked with one parameter containing the following properties:

group
  Reference to the data object which is subject to exposure state change.
exposed
  `true` if the group has just been exposed, `false` otherwise
indirect
  `true` if the exposure change is a side effect of some other user interaction, such as view reset.

Note that one user action may result in many events of this type, for example unexposure of currently exposed groups and exposure of another group. The global exposure state is easier to track using `onGroupExposureChanged` event.

When multiple callbacks are present the entire, set will be invoked, even if one or more callbacks changes the exposure via an API call.

The listener will not be invoked for API-initiated state changes, only for user interactions. In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupExposureChanged

Called once after the exposure has changed. The callback receives one parameter: an object containing the following properties:

groups
  an array of references to the data objects representing the groups that are currently exposed
indirect
  `true` if the exposure change is a side effect of some other user interaction, such as view reset.

When multiple callbacks are present, the entire set will be invoked, even if one or more callbacks changes the exposure via an API call.

The listener will not be invoked for API-initiated state changes, only for user interactions. In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

    foamtree.set({
      onGroupExposureChanged: function(info) {
        console.log(info);
      }
    });

### onGroupOpenOrCloseChanging

Called during group opening state changes. For each opened or closed group, the listener will be invoked with one parameter containing the following properties:

group
  Reference to the data object which is subject to opening state change.
open
  `true` if the group has just been opened, `false` otherwise
indirect
  `true` if the state change is a side effect of some other user interaction, such as group exposure change or view reset.

Note that one user action may result in many events of this type, for example closing of currently opened groups and opening of another group. The global state is easier to track using `onGroupOpenOrCloseChanged` event.

When multiple callbacks are present the entire, set will be invoked, even if one or more callbacks changes the state via an API call.

The listener will not be invoked for API-initiated state changes, only for user interactions. In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupOpenOrCloseChanged

Called once after some groups have been opened or closed. The callback receives one parameter: an object containing the following properties:

groups
  an array of references to the data objects representing the groups that are currently open
indirect
  `true` if the state change is a side effect of some other user interaction, such as group exposure change or view reset.

When multiple callbacks are present, the entire set will be invoked, even if one or more callbacks changes the exposure via an API call.

The listener will not be invoked for API-initiated state changes, only for user interactions. In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

    foamtree.set({
      onGroupOpenOrCloseChanged: function(info) {
        console.log(info);
      }
    });

## Interaction events

This section documents events related to low-level interactions initiated by the user. Listeners registered for the those events will receive the event details object that they may use to prevent the default action associated with the specific event.

### onGroupClick

Called after a mouse click (or tap gesture) was detected on a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

The following example shows how to make certain group unselectable.

    foamtree.set({
      dataObject: { groups: [
        { label: "Selectable" },
        { label: "Selectable" },
        { label: "Selectable" },
        { label: "Unselectable", unselectable: true },
        { label: "Unselectable", unselectable: true }
      ]},
      onGroupClick: function (event) {
        if (event.group.unselectable) {
          event.preventDefault();
        }
      },
      groupColorDecorator: function (opts, props, vars) {
        vars.groupColor = props.group.unselectable ? "#aaa" : "#77ff00";
        vars.labelColor = props.group.unselectable ? "#888" : "auto";
      }
    });

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupDoubleClick

Called after a double click (or double tap gesture) was detected on a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupHold

Called after a click-and-hold (or tap-and-hold gesture) was detected on a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupHover

Called after the mouse pointer enters the area covered by a group or leaves the visualization area completely. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action, which is highlighting of the hovered-on group.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupMouseMove

Called when the mouse pointer moves over the area of some group. The provided callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group and screen- and visualization-relative coordinates of the mouse pointer.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

You can use this event in combination with `groupContentDecorator` to make the custom-drawn elements of each group interactive. See the Interactive custom content demo for some example implementation.

### onGroupMouseWheel

Called after the mouse wheel is rotated over a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupMouseDown

Called after the mouse pointer is pressed. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupMouseUp

Called after the mouse pointer is released. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupDragStart

Called after the user starts dragging a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupDrag

Called when the user is dragging a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupDragEnd

Called when group dragging is complete. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupTransformStart

Called after the user starts touch-based zooming over a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupTransform

Called when the user is performing touch-based zooming over a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onGroupTransformEnd

Called when the user has completed touch-based zooming over a group. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

### onKeyUp

Called when the user pressed a key. The callback will be invoked with one parameter – the event details object. You can use that object to get more information about the affected group. You can also use it to prevent the default action.

In the context of the callback, `this` points to the involved instance of `CarrotSearchFoamTree`.

## Geometry utils

FoamTree exposes a number of geometry utilities useful when implementing `groupContentDecorator`s. The utilities are grouped into a static object available from `CarrotSearchFoamTree.geometry`. Please see below for a detailed list of utility methods.

### boundingBox(points)

Given an array of points, returns the bounding box of the points.

The method is available statically as:

`CarrotSearchFoamTree.geometry.boundingBox(points)`

where:

points

  the array of point coordinates. Each element of the array must be an object containing the `x` and `y` properties representing the coordinates of the point.

<return value>

  an object representing the bounding box. The object contains the `x` and `y` properties denoting the coordinates of the top-left corner of the rectangle, as well as the `w` and `h` properties denoting the width and height of the rectangle, respectively.

### polygonCentroid(polygon)

Computes the centroid (center of mass) and area of the provided polygon.

The method is available statically as:

`CarrotSearchFoamTree.geometry.polygonCentroid(polygon)`

where:

polygon

  the input polygon. The polygon must be specified as an array of objects, each object containing the `x` and `y` property representing the coordinates of the polygon's vertex.

  The polygon must be non-self-intersecting, vertex coordinates must be enumerated in clockwise or anti-clockwise order. For performance reasons, this method will not verify if the criteria are met. If the provided polygon does not satisfy the criteria, the result will be unspecified. Note that the polygons returned when getting the `geometry` option meet both criteria.

<return value>

  an object containing the `x` and `y` properties denoting the coordinates of the centroid as well as the `area` property containing the area of the polygon in pixels.

### rectangleInPolygon(polygon, cx, cy, wTH, scale, fx, fy)

Given a convex polygon, finds the largest inscribed rectangle of given aspect ratio `wTH`, centered around the provided center point (`cx`, `cy`). Additionally, the resulting rectangle can be scaled by the provided `scale` factor. Also, the alignment of the center point relative to the rectangle can be defined by the (`fx`, `fy`) point.

The method is available statically as:

`CarrotSearchFoamTree.geometry.rectangleInPolygon(polygon, cx, cy, wTH, scale, fx, fy)`

where:

polygon

  the polygon to inscribe the rectangle into. The polygon must be specified as an array of objects, each object containing the `x` and `y` property representing the coordinates of the polygon's vertex.

  The polygon must be convex, vertex coordinates must be enumerated in clockwise or anti-clockwise order. For performance reasons, this method will not verify if the criteria are met. If the provided polygon does not satisfy the criteria, the result will be unspecified. Note that the polygons returned when getting the `geometry` option meet both criteria.

cx, cy

  coordinates of the point inside the polygon around which the rectangle should be centered. For performance reasons, this method will not verify if the point lies inside the polygon. For points outside or on the boundary of the polygon, the result is not specified.

wTH

  (optional) the aspect ratio of the polygon to inscribe, the ratio is assumed to be the polygons width divided by the polygon's height. If not provided, the ratio of `1.0` will be used, which will produce a square.

scale

  (optional) the scale factor to apply to the resulting rectangle. The rectangle will be scaled relative to the alignment point determined by (`fx`, `fy`). If not provided, the scale of `1.0` will be used, which will produce the largest possible rectangle that fits inside the polygon and meets the center and alignment criteria.

fx, fy

  (optional) defines what the the position of the center point (`cx`, `cy`) should be relative to the rectangle. The default value is (`0.5`, `0.5`), which will position the rectangle in such a way that the center point is at the center of the rectangle. For (`fx`, `fy`) = (`0.0`, `0.0`), the center point will overlap with the top-left corner of the rectangle. Similarly, when (`fx`, `fy`) = (`1.0`, `1.0`), the center point will be at the bottom-right corner of the rectangle. Values larger than `1.0` or lower than `0.0` are also supported, in which case the center point will lie outside of the rectangle.

<return value>

  an object representing the produced rectangle. The object contains the `x` and `y` properties denoting the coordinates of the top-left corner of the rectangle, as well as the `w` and `h` properties denoting the width and height of the rectangle, respectively.

The following example demonstrates the usage and results of this method.

    foamtree.set({
      dataObject: { groups: [
        { ratio: 1.0, shift: 0.0, scale: 1.0, fx: 0.5, fy: 0.5 },
        { ratio: 1.0, shift: 0.0, scale: 0.7, fx: 0.5, fy: 0.5 },
        { ratio: 1.0, shift: 0.0, scale: 0.5, fx: 0.5, fy: 0.5 },
        { ratio: 1.0, shift: 0.0, scale: 1.0, fx: 0.0, fy: 0.0 },
        { ratio: 1.0, shift: 0.0, scale: 1.0, fx: 1.0, fy: 1.0 },
        { ratio: 1.0, shift: 0.0, scale: 1.0, fx: 0.2, fy: 0.7 },
        { ratio: 1.0, shift: 0.0, scale: 1.0, fx: 0.2, fy: -0.5 },
        { ratio: 1.0, shift: 0.0, scale: 1.0, fx: 0.5, fy: 1.5 },
        { ratio: 2.0, shift: 0.0, scale: 1.0, fx: 0.5, fy: 0.5 },
        { ratio: 0.5, shift: 0.0, scale: 1.0, fx: 0.5, fy: 0.5 },
        { ratio: 1.0, shift: 0.2, scale: 1.0, fx: 0.5, fy: 0.5 },
        { ratio: 1.0, shift: 0.5, scale: 1.0, fx: 0.5, fy: 0.5 },
        { ratio: 1.0, shift: 0.7, scale: 1.0, fx: 0.5, fy: 0.5 }
      ]},
      groupContentDecorator: function (opts, params, vars) {
        // Prepare some variables
        var centerX = params.polygonCenterX;
        var centerY = params.polygonCenterY;
        var polygon = params.polygon;
        var scale = params.group.scale;
        var shift = params.group.shift;
        var fx = params.group.fx, fy = params.group.fy;

        if (shift > 0) {
          // Shift towards one vertex to make sure the point stays within the polygon
          centerX = centerX * (1 - shift) + polygon[0].x * shift;
          centerY = centerY * (1 - shift) + polygon[0].y * shift;
        }

        // Compute the inscribed rectangle
        var box = CarrotSearchFoamTree.geometry.rectangleInPolygon(
          polygon, centerX, centerY, params.group.ratio, scale, fx, fy);

        // Draw the rectangle
        var ctx = params.context;
        ctx.strokeStyle = "rgba(0, 0, 0, 0.5)";
        ctx.lineWidth = 2;
        ctx.strokeRect(box.x, box.y, box.w, box.h);

        // Draw the center point
        ctx.beginPath();
        ctx.arc(centerX, centerY, 5, 0, 2 * Math.PI);
        ctx.fill();

        // Draw the parameters too
        var fontSize = (params.boxHeight / 30);
        ctx.font = fontSize.toFixed(2) + "px Tahoma, sans-serif";
        ctx.textAlign = "center";
        ctx.fillText("(fx, fy) = (" + fx + ", " + fy + ")", centerX + 0.5 * fontSize, centerY - fontSize);
        ctx.fillText("scale = " + scale, centerX + 0.5 * fontSize, centerY - fontSize * 2);
      },
      titleBarDecorator: function (opts, params, vars) {
        // Display the details in the title bar on hover
        vars.titleBarShown = true;
        vars.titleBarText =
          "widthToHeight = " + params.group.ratio + "   " +
            "shiftFromCenter = " + params.group.shift + "   " +
            "margin = " + params.group.margin;
      }
    });

### circleInPolygon(polygon, cx, cy)

Given a convex polygon, finds the largest inscribed circle centered around the provided point.

The method is available statically as:

`CarrotSearchFoamTree.geometry.circleInPolygon(polygon, cx, cy)`

where:

polygon

  the polygon to inscribe the circle into. The polygon must be specified as an array of objects, each object containing the `x` and `y` property representing the coordinates of the polygon's vertex.

  The polygon must be convex, vertex coordinates must be enumerated in clockwise or anti-clockwise order. For performance reasons, this method will not verify if the criteria are met. If the provided polygon does not satisfy the criteria, the result will be unspecified. Note that the polygons returned when getting the `geometry` option meet both criteria.

cx, cy

  coordinates of the point inside the polygon around which the circle should be centered. For performance reasons, this method will not verify if the point lies inside the polygon. For points outside or on the boundary of the polygon, the result is not specified.

<return value>

  the radius of the inscribed circle in pixels.

The following example demonstrates the usage and results of this method.

    foamtree.set({
      dataObject: { groups: [
        { shift: 0.0, margin: 0.0 },
        { shift: 0.0, margin: 0.2 },
        { shift: 0.0, margin: 0.4 },
        { shift: 0.0, margin: 0.6 },
        { shift: 0.2, margin: 0.0 },
        { shift: 0.4, margin: 0.0 },
        { shift: 0.6, margin: 0.0 }
      ]},
      groupContentDecorator: function (opts, params, vars) {
        // Prepare some variables
        var centerX = params.polygonCenterX;
        var centerY = params.polygonCenterY;
        var polygon = params.polygon;
        var margin = params.group.margin;
        var shift = params.group.shift;

        if (shift > 0) {
          // Shift towards one vertex to make sure the point stays within the polygon
          centerX = centerX * (1 - shift) + polygon[0].x * shift;
          centerY = centerY * (1 - shift) + polygon[0].y * shift;
        }

        // Compute the inscribed circle
        var radius = CarrotSearchFoamTree.geometry.circleInPolygon(
          polygon, centerX, centerY) * (1 - margin);

        // Draw the circle
        params.context.lineWidth = 2;
        params.context.beginPath();
        params.context.arc(centerX, centerY, radius, 0, 2 * Math.PI);
        params.context.stroke();

        // Draw the center point
        params.context.beginPath();
        params.context.arc(centerX, centerY, 5, 0, 2 * Math.PI);
        params.context.fill();
      },
      titleBarDecorator: function (opts, params, vars) {
        // Display the details in the title bar on hover
        vars.titleBarShown = true;
        vars.titleBarText =
          "shiftFromCenter = " + params.group.shift + "   " +
            "margin = " + params.group.margin;
      }
    });

### stabPolygon(polygon, cx, cy, angle)

Splits the given convex polygon into two polygons along the line defined by the provided point and angle.

The method is available statically as:

`CarrotSearchFoamTree.geometry.stabPolygon(polygon, cx, cy, angle)`

where:

polygon

  the polygon to split. The polygon must be specified as an array of objects, each object containing the `x` and `y` property representing the coordinates of the polygon's vertex.

  The polygon must be convex, vertex coordinates must be enumerated in clockwise or anti-clockwise order. For performance reasons, this method will not verify if the criteria are met. If the provided polygon does not satisfy the criteria, the result will be unspecified. Note that the polygons returned when getting the `geometry` option meet both criteria.

cx, cy

  coordinates of the point that defines the splitting line.

angle

  the angle defining the splitting line, in radians.

<return value>

  an array of two polygons being the result of the split or `undefined` if the provided line does not intersect the provided polygon.

  An example result is shown in the figure, where one of the resulting polygons (drawn in white dashed line) has 4 vertices and the other (drawn in black dashed line) has 5 vertices.

  Please note the following properties of the returned polygons:

  - Each polygon is represented by an array of objects representing the polygon's vertices; each such object has the `x` and `y` properties defining the coordinates of the vertex.
  - Both returned polygons are convex with vertices enumerated in the clockwise order.
  - The returned polygons have one edge in common as shown by the black-white dashed in the figure. The two points defining the common edge in both polygons are always at indices `0` and `1` in the corresponding arrays, although not in the same order to preserve the clockwise enumeration order of the vertices.
  - The order of the two resulting polygons in the returned array is deterministic. For example, if `angle = 0`, the polygon at index `0` will be the upper one, polygon at index `1` will be the lower one. When the angle becomes 90 degrees, polygon at index `0` will be on the left, polygon at index `1` will be on the right.
  - If the provided line overlaps with any edge of the provided polygon or contains any vertex of the provided polygon, the returned polygons may be degenerate (e.g. have zero area and duplicated vertices).

The following example demonstrates the usage and results of this method.

    foamtree.set({
      dataObject: {
        groups: (function () {
          var groups = [];
          for (var i = 0; i < 24; i++) {
            groups.push({ angle: Math.PI * i / 24 });
          }
          return groups;
        })()
      },
      relaxationInitializer: "squarified",
      groupBorderRadius: 0,

      groupContentDecorator: function (opts, params, vars) {
        // Split the polygon along its center
        var split = CarrotSearchFoamTree.geometry.stabPolygon(params.polygon,
          params.polygonCenterX, params.polygonCenterY, params.group.angle);

        // If splitting was successful, draw both polygons
        if (split) {
          var j;
          var ctx = params.context;
          ctx.setLineDash([8, 8]);

          ctx.fillStyle = "blue";
          ctx.beginPath();
          ctx.arc(params.polygonCenterX, params.polygonCenterY, 5, 0, Math.PI * 2);
          ctx.fill();

          // Draw the first polygon
          ctx.lineWidth = 3;
          ctx.strokeStyle = "white";
          ctx.beginPath();
          ctx.moveTo(split[0][0].x, split[0][0].y);
          for (j = 1; j < split[0].length; j++) {
            ctx.lineTo(split[0][j].x, split[0][j].y);
          }
          ctx.closePath();
          ctx.stroke();

          // Draw the second polygon. We set a line dash offset
          // and the use unusual iteration order to draw the common
          // edge of the two polygons in a coherent way (start and end
          // the line in at the same points). This will ensure that
          // the dashed elements don't overlap.
          ctx.lineDashOffset = 8;
          ctx.strokeStyle = "black";
          ctx.beginPath();
          ctx.moveTo(split[1][1].x, split[1][1].y);
          var len = split[1].length;
          for (var i = len - 1; i >= 1; i--) {
            var p = split[1][(i + 1) % len];
            ctx.lineTo(p.x, p.y);
          }
          ctx.closePath();
          ctx.stroke();
        }
      }
    });

## Drawing context

The `groupContentDecorator` exposes a drawing context buffer that must be used do draw the custom content of the polygon. While the provided context behaves like the standard CanvasRenderingContext2D, it also exposes a number of useful non-standard methods.

### roundRect(x, y, w, h, r)

Prepares a path of a rectangle with rounded corners. The path can then be filled and/or stroked depending on the needs.

This method accepts the following parameters:

x, y

  coordinates of the top-left corner of the rectangle.

w, h

  width and height of the rectangle, in pixels.

r

  radius of the corners, in pixels.

The following example demonstrates the this method.

    foamtree.set({
      dataObject: { groups: randomGroups(30) },

      groupContentDecorator: function (opts, params, vars) {
        var box = CarrotSearchFoamTree.geometry.rectangleInPolygon(
          params.polygon, params.polygonCenterX, params.polygonCenterY,
          4, 0.8);

        var ctx = params.context;
        ctx.roundRect(box.x, box.y, box.w, box.h, box.h * 0.1);
        ctx.fillStyle = "rgba(255, 255, 255, 0.4)";
        ctx.fill();
        ctx.strokeStyle = "rgba(255, 255, 255, 0.7)";
        ctx.stroke();
      }
    });

### fillPolygonWithText(polygon, cx, cy, text, options)

Fills the provided polygon with text. If needed, this method will split the text into multiple lines and then record the appropriate `fillText()` calls to the context on which the method was called.

Please note that it may happen that the method is unable to fill the polygon with text given the provided set of options. Therefore, the callers must check the information object returned by this method to ensure the text was actually fit in the polygon.

This method accepts the following parameters:

polygon

  the polygon to fill the text with. The polygon must be specified as an array of objects, each object containing the `x` and `y` property representing the coordinates of the polygon's vertex.

  The polygon must be convex, vertex coordinates must be enumerated in the clockwise order. For performance reasons, this method will not verify if the criteria are met. If the provided polygon does not satisfy the criteria, the result will be unspecified. Note that the polygons returned when getting the `geometry` option meet both criteria.

cx, cy

  the center point that determines the location of the text within the polygon.

text

  the text to fill the polygon with. This method supports a number of special characters in the text.

options

  Additional options for this method:

  fontFamily
    the font family to assume when filling the polygon with text, `sans-serif` by default.
  fontWeight
    Since 3.2.2 the font weight (e.g. `bold`) to assume when filling the polygon with text, `normal` by default.
  fontStyle
    Since 3.2.2 the font style (e.g. `italic`) to assume when filling the polygon with text, `normal` by default.
  fontVariant
    Since 3.2.2 the font variant (e.g. `small-caps`) to assume when filling the polygon with text, `normal` by default.
  minFontSize
    the minimum font size in pixels to use for filling the text, `0` by default.
  maxFontSize
    the maximum font size in pixels to use for filling the text, `72` by default.
  lineHeight
    the line height to assume when splitting the text into mulitple lines, `1.05` by default. Line heights smaller than `1.0` are currently not supported.
  verticalAlign
    the vertical align of the text relative to the center point (`cx`, `cy`), `center` by default. The following allignments are supported:
    top
      the top line of the text will be at the `cy` coordinate of the center point
    bottom
      the bottom line of the text will be at the `cy` coordinate of the center point
    center
      the text will be centered vertically around the center point

    Please note that currently the horizontal align option is not available, the text will always be centered horizontally.

  horizontalPadding
    the horizontal padding to apply to each line of text. The unit of this option is the font size used to render the text. Default value: `1.0`.
  verticalPadding
    the vertical padding to apply to the whole block of text. The unit of this option is the font size used to render the text. Default value: `0.5`.
  maxTotalTextHeight
    The maximum total height of the text block as a fraction of the polygon's bounding box height, `0.95` by default.
  allowEllipsis
    if `true` and the complete text does not fit in the polygon, fill as much text as possible and append the ellipsis to replace the non-fitting part of the text, `false` by default.
  allowForcedSplit
    if `true` the text can be split at arbitrary points, such as inside words, during the filling process, `false` by default.
  cache

    if a non-null object is provided, this method will use it to cache some geometry information to speed up the text filling process. When another text filling request is made and the same cache object is presented, this method will try to reuse the cached information to speed up the layout process. If, however, the geometry of the polygon changes a lot, the cache will not be used and a complete filling process will be performed.

    For caching to work properly, dedicated cache objects need to be provided for each logical polygon. For example, each group polygon will require a separate cache object. Moreover, to perform caching, the `area` option must also be provided.

    Default value: `undefined`, meaning no caching will be performed.

  area
    the current area of the polygon for which the filling is performed. The area must only be provided if layout caching is desired.
  cacheInvalidationThreshold
    determines how much the area of the polygon must change in order to discard the cached layout and invoke the complete text fitting process, `0.05` by default.
  invalidate
    if `true`, the cache will be invalidated and full text filling process will be performed, regardless of whether the geometry of the polygon changed or not. If the label text or font has changed, you must set the `invalidate` option to `true` to ensure the text fitting is correctly performed.
<return value>

  an object summarizing the text filling process:

  fit
    `true` when the text fit in the polygon. If this property is `false`, then no text drawing commands were issued and the returned summary object does not have any other properties.
  lineCount
    the number of lines into which the text was split.
  fontSize
    the font size, in pixels, in which the text was drawn.
  box
    the bounding box of the complete text block, an object with the `x`, `y`, `w` and `h` properties representing the top-left corner coordinates, width and height, respectively.
  ellipsis
    `true` if the ellipsis was applied at the end of the text.

The following example demonstrates the results of invoking this method with different options.

    foamtree.set({
      dataObject: { groups: [
        { align: "center", minFontSize: 0, ellipsis: false,
          text: "Some short text" },

        { align: "center", minFontSize: 0, ellipsis: false,
          text: "Some italic text", style: "italic" },
        { align: "center", minFontSize: 0, ellipsis: false,
          text: "Some bold text", weight: "bold" },
        { align: "center", minFontSize: 0, ellipsis: false,
          text: "Some small-caps text", variant: "small-caps" },

        { align: "center", minFontSize: 0, ellipsis: false,
          text: "Some text longer by a small bit" },

        { align: "center", minFontSize: 0, ellipsis: false,
          text: "A rather long text which should still fit the polygon pretty comfortably" },

        { align: "center", minFontSize: 40, ellipsis: false,
          text: "A rather long text that will most likely not fit the polygon with some " +
                "large minFontSize. A rather long text that will most likely not fit " +
                "the polygon with some minFontSize."},

        { align: "center", minFontSize: 40, ellipsis: true,
          text: "A rather long text that will most likely require an ellipsis to fit the " +
                "polygon at some large minFontSize. A rather long text that will most likely " +
                "require an ellipsis to fit the polygon at some large minFontSize." },

        { align: "center", minFontSize: 0, ellipsis: false,
          text: "Testing vertical align of 'center'" },

        { align: "top",    minFontSize: 0, ellipsis: false,
          text: "Testing vertical align of 'top'" },

        { align: "bottom", minFontSize: 0, ellipsis: false,
          text: "Testing vertical align of 'bottom'" }
      ]},

      groupContentDecorator: function (opts, params, vars) {
        var ctx = params.context;

        var group = params.group;
        var fillTextInfo = ctx.fillPolygonWithText(
          params.polygon,
          params.polygonCenterX, params.polygonCenterY,
          group.text, {
            fontWeight: group.weight || "normal",
            fontStyle: group.style || "normal",
            fontVariant: group.variant || "normal",
            minFontSize: group.minFontSize,
            allowEllipsis: group.ellipsis,
            verticalAlign: group.align
          });

        if (!fillTextInfo.fit) {
          // Draw a red cross instead
          var box = CarrotSearchFoamTree.geometry.rectangleInPolygon(params.polygon,
            params.polygonCenterX, params.polygonCenterY, 1.0, 0.8);
          ctx.lineWidth = 3;
          ctx.strokeStyle = "red";
          ctx.moveTo(box.x, box.y);
          ctx.lineTo(box.x + box.w, box.y + box.h);
          ctx.moveTo(box.x + box.w, box.y);
          ctx.lineTo(box.x, box.y + box.h);
          ctx.stroke();
        }
      }
    });

### scratch()

Creates and returns a temporary drawing context buffer. Combined with the `replay()` method, the temporary contexts can be useful in the following scenarios:

1. Drawing a rectangle beneath the text that was fitted in a polygon. The `fillPolygonWithText()` method issues the drawing commands immediately, but in order to draw a rectangle beneath the text, we need to know the bounding box of the text in advance. The solution is to invoke the text filling on the temporary context, draw the rectangle based on the text filling info returned by `fillPolygonWithText()` and finally replay the temporary text buffer buffer to draw the text on top of the rectangle. The following example demonstrates this approach.

         foamtree.set({
           dataObject: { groups: randomGroups(20) },

           groupContentDecorator: function (opts, params, vars) {
             var ctx = params.context;
             var group = params.group;

             var scratch = ctx.scratch();
             var fillTextInfo = ctx.fillPolygonWithText(
               params.polygon,
               params.polygonCenterX, params.polygonCenterY,
               "Group\u00a0" + (params.index + 1));

             if (fillTextInfo.fit) {
               // Draw the rectangle first
               var box = fillTextInfo.box;
               var padding = box.h * 0.15;
               ctx.lineWidth = 2;
               ctx.roundRect(box.x - padding, box.y - padding,
                 box.w + 2 * padding, box.h + 2 * padding, padding);
               ctx.globalAlpha = 0.2;
               ctx.fill();
               ctx.globalAlpha = 0.6;
               ctx.stroke();
               scratch.replay(ctx);
             }
           }
         });

2. Caching expensive layout computations. When `groupContentDecoratorTriggering` is `onSurfaceDirty`, the content decorator will be invoked each time the visualization needs to be redrawn. During most of those calls, the geometry of the groups' polygons will remain the same, so there may be no need recompute the layout of the custom content. One way to cache the layout in such cases would be to create one or more drawing context buffers per group, fill the buffers only when the `shapeDirty` property of the decorator is `true` and when `shapeDirty` is `false`, replay the previously buffered drawing commands. Please see the GitHub search example for this pattern applied in practice.

### replay(targetContext)

Replays the content of the drawing context buffer to the `targetContext`. The target context can either be another context buffer of the actual native drawing context. For usage scenarios and examples, please see the `scratch()` method.

# Release notes

## Version 3.6.1

released on 09.04.2026

Version 3.6.1 fixes an infinite loop in `reset()` that could occur when a zoom animation was initiated but `reset()` was called before the first animation frame ran, for example:

    foamtree.zoom(group);
    foamtree.reset();

The 3.6.1 release is a drop-in replacement for the 3.6.0 release.

## Version 3.6.0

released on 01.04.2026

The 3.6.0 release brings a completely rewritten layout engine, delivering dramatically faster computation, near-perfect polygon sizing accuracy and a number of smaller improvements and bug fixes.

The 3.6.0 release is a drop-in replacement for the earlier 3.5.x releases, unless your code uses any of the `groupGrowingDuration`, `groupGrowingEasing` and `groupGrowingDrag`, which this release removes.

### New features

Faster layout engine

  The layout engine has been rewritten from scratch. The new implementation is 10× faster for typical diagrams (20–50 groups) and up to 300× faster for very large diagrams (1000 groups).

Perfect polygon sizing

  Polygon areas now match input weights with near-perfect accuracy (typically within 0.01%), regardless of the relaxation quality setting. The quality threshold controls polygon roundness only — area accuracy is always guaranteed. See the updated polygon sizing accuracy demo.

  Note: for data sets with very unbalanced weights, the improved accuracy may result in some groups appearing much smaller than in previous releases, which could not fully represent large weight differences. If this is undesirable, consider flattening the weight distribution before passing it to FoamTree — for example, using `Math.sqrt(weight)` instead of the raw weight. This produces a visually more balanced layout at the cost of no longer representing the exact input proportions.

### Improvements

Suppressible description
groups

  In flattened stacking, the extra space reserved for a group’s label can now be suppressed on a per-group basis by setting the group’s `description` property to `false`. This gives child groups more room when no label is needed. See the suppressing description groups demo.

### Bug fixes

Choppy mouse wheel
zoom on macOS

  Fixed choppy, non-animated zooming when using a mouse wheel on macOS.

Alpha in CSS3 `groupColor`

  Fixed `groupColorDecorator` ignoring the alpha channel when `groupColor` was set to a CSS3 string (e.g. `rgba()` or `hsla()`).

### API changes

Weight growing
options removed

  The `groupGrowingDuration`, `groupGrowingEasing` and `groupGrowingDrag` options have been removed. The new layout engine is incompatible with animated weight transitions. Weight changes via the `update()` method are now applied immediately; the relaxation animation (controlled by `relaxationVisible`) provides visual feedback during re-layout.

## Version 3.5.7

released on 20.10.2025

Version 3.5.7 adds the `drawTo()` method, which you can use to export the current visualization image as SVG.

The 3.5.7 release is a drop-in replacement for the earlier 3.5.x releases.

## Version 3.5.6

released on 17.07.2025

Version 3.5.6 adds the `onViewResetting` event, which you can use to prevent the default actions associated with the view reset interactions.

The 3.5.6 release is a drop-in replacement for the earlier 3.5.x releases.

## Version 3.5.5

released on 11.07.2025

Version 3.5.5 tunes the display details level for smoother animations on mobile devices.

The 3.5.5 release is a drop-in replacement for the earlier 3.5.x releases.

## Version 3.5.4

released on 03.07.2025

The 3.5.4 enables rendering more group details, such as borders and gradients, by default.

The 3.5.4 release is a drop-in replacement for the earlier 3.5.x releases.

### Bug fixes

Missing group rendering details

  Version 3.5.3 might not render certain group details, such as borders or gradients. Version 3.5.4 fixes the issue.

## Version 3.5.3

released on 02.07.2025

The 3.5.3 improves FoamTree container resizing behavior and improves startup performance.

The 3.5.3 release is a drop-in replacement for the earlier 3.5.x releases.

### Improvements

Container resizing

  Version 3.5.3 adds the `resizeTransform` property with an option to preserve the relative locations of groups after multiple `resize` events.

### Bug fixes

First-time initialization delay

  Previous versions might block the browser for a significant time during the first-time initialization of FoamTree on the device. Version 3.5.3 fixes the issue.

## Version 3.5.2

released on 19.03.2025

The 3.5.2 release fixes minor rendering bugs.

The 3.5.2 release is a drop-in replacement for the earlier 3.5.x releases.

### Bug fixes

Disappearing
exposed groups

  In previous versions, exposing a child group and then zooming the visualization around the exposed group, might cause the group to disappear on very high zoom levels. Version 3.5.2 fixes the issue.

JavaScript errors
on high zoom levels

  The 3.5.2 release fixes a bug that might cause rendering failures and JavaScript errors on high zoom levels. The bug only affects the demo version of FoamTree, which displays the FoamTree logo. Licensed versions of FoamTree are not affected by this bug.

### Improvements

Documentation
improvements

  Version 3.5.2 clarifies the interactions between flattened `stacking` and the `maxGroupLevelsAttached` property.

## Version 3.5.1

released on 18.04.2023

The 3.5.1 release fixes a code minification issue that may FoamTree to fail to initialize with certain bundlers.

## Version 3.5.0

released on 19.01.2021

The 3.5.0 release exposes FoamTree as an NPM dependency for modern software building systems.

The 3.5.0 release is a drop-in replacement for the earlier 3.4.x releases.

### New features

NPM dependency

  Starting with version 3.5.0, in addition to the conventional downloaded package, FoamTree is also be available as an NPM dependency. Both the branded demo and the non-branded licensed build is available in the new format.

### Improvements

Attribution group interaction
  Previous versions would open the attribution URL immediately after clicking the attribution group. Since version 3.5.0 a ctrl+click is required to open the URL.

## Version 3.4.10

released on 29.11.2019

The 3.4.10 makes it possible to customize the initial position of groups.

The 3.4.10 release is a drop-in replacement for the earlier 3.4.x releases.

### New features

Custom initial group position
  Version 3.4.10 makes it possible to customize the initial positions of specific groups. See the initialPosition property of the group data object for more details.

### Bug fixes

JavaScript error on Android
and large zoom level
  Previous versions would throw JavaScript errors and lock up when crossing a certain large zoom level on Android devices. This release fixes the problem.
Legacy Android browser
workaround disabled
  A workaround for a bug in legacy Android stock browser is now disabled by default.

## Version 3.4.9

released on 10.09.2019

The 3.4.9 fixes a number of minor issues.

The 3.4.9 release is a drop-in replacement for the earlier 3.4.x releases.

### New features

Description groups in
hierarchical stacking
  Version 3.4.9 makes it possible to allocate extra space for group descriptions also in hierarchically-stacked layouts. See the `descriptionGroup` option for more details and code examples.
Updating weights of
individual groups

  As of version 3.4.9, the `update` method can take an optional parameter specifying the list of groups whose weights to update.

### Bug fixes

groupLabelDrawn
impossible to reset

  Previous releases would only let `groupContentDecorator` set the `groupLabelDrawn` variable to `false`. Setting the variable to `true` would not work. Version 3.4.9 fixes the issue.

## Version 3.4.8

released on 04.07.2019

The 3.4.8 fixes an important performance issue affecting visualizations in flattened stacking mode, upgrade is strongly recommended. This version also documents how to combine lazy loading of groups with flattened stacking mode.

The 3.4.8 release is a drop-in replacement for the earlier 3.4.x releases.

### Bug fixes

Full redraws in
flattened stacking mode

  Previous versions would trigger a full visualization redraw when hovering over top-level groups in flattened stacking mode. This could be a significant performance issue on larger data sets. Version 3.4.8 fixes the issue and issues incremental redraws whenever possible.

Mouse wheel listener
declared as non-passive

  As of version 3.4.8, FoamTree declares the internal mouse wheel event listener as non-passive to avoid browser warnings.

Lazy loading in flattened
stacking mode

  Documentation of previous versions incorrectly stated that lazy loading was not supported in flattened stacking mode. In fact, version 3.4.5 exposed the attach() method that makes lazy loading possible with flattened stacking.

  The lazy loading code example has been updated to work with both stacking modes.

## Version 3.4.7

released on 23.05.2019

The 3.4.7 fixes a number of small software bugs related to flattened stacking mode and visualization resizing.

The 3.4.7 release is a drop-in replacement for the earlier 3.4.x releases.

### Bug fixes

FoamTree may disappear
on resize

  Previous versions are affected by a bug that can cause FoamTree to disappear when the container element is resized. The 3.4.7 release fixes the issue.

Hovering over exposed
flattened groups

  When in flattened stacking mode, hovering over some exposed groups would highlight the non-exposed groups beneath the exposed ones. Version 3.4.7 correctly highlights the exposed group on top.

Double-click detection

  Previous versions may fail to detect double-double click event in some areas of the visualization when the display is stretched, that is FoamTree container changed its size, but resize() wasn't called. Version 3.4.7 fixes the issue.

Label rendering in
flattened mode

  When `stacking` is set to `flattened` and group hierarchy is deeper than `maxGroupLevelsDrawn`, FoamTree might not render labels of the deepest visible groups. Version 3.4.7 fixes the issue.

## Version 3.4.6

released on 28.12.2018

The 3.4.6 fixes a number of small software bugs and documentation omissions.

The 3.4.6 release is a drop-in replacement for the earlier 3.4.x releases.

### Bug fixes

JavaScript errors
when calling dispose()

  Previous versions might throw JavaScript errors when `dispose()` was called with pullback animations running at the same time. Version 3.4.6 fixes the issue.

Legacy mouse wheel events

  Previous versions would use the non-standard `mousewheel` event which would prevent Chrome from reacting on the standard `wheel` events in other parts of the page. Version 3.4.6 fixes the issue by switching to listening to the standard `wheel` event.

### Documentation extensions

`description` property

  Documentation of `description` property has been added to the `hierarchy` option documentation.

## Version 3.4.5

released on 26.07.2017

The 3.4.5 adds a number of minor improvements, bug fixes and documentation extensions.

The 3.4.5 release is a drop-in replacement for the earlier 3.4.x releases.

### Improvements

Deferred layout
of child groups

  You can use the `maxGroupLevelsAttached` option along with the `attach` method to defer the layout of lower hierarchy levels until the layout of the top level is computed and shown to the user. This can improve the responsiveness of the visualization during the initial data loading. See the Deferred layout demo for a complete code example.

Performance tuning
tips added

  The layout performance tuning section has been extended with tips about deferred layout computation and replacing expose with zoom in.

### Bug fixes

Lazy initialization
of child groups
on group open

  With lazy initialization of child groups enabled, FoamTree would not properly initialize child groups of a parent group whose opening would cause the `maxGroupLevelsDrawn` threshold to be met. The 3.4.5 release fixes the issue.

## Version 3.4.4

released on 26.09.2016

The 3.4.4 adds a number of minor improvements, bug fixes and documentation extensions.

The 3.4.4 release is a drop-in replacement for the earlier 3.4.x releases.

### Improvements

onGroupMouseMove event
and absolute coordinates

  The 3.4.4 release introduces the `onGroupMouseMove` event that is continuously triggered when the user's mouse pointer hovers over some group. Additionally, the event details object will now contain the `xAbsolute` and `yAbsolute` properties that are visualization relative and independent of zoom, pan and expose transformations.

  You can use the new event and visualization-relative coordinates to make the custom group content interactive.

Container-relative
coordinate retrieval

  You can use the `containerCoordinates` option to retrieve HTML container-relative coordinates of a specific point inside FoamTree visualization. You can use this option to implement, for example, sticky tooltips that always point at a specific point in the visualization, regardless of zoom, pan or expose transformations.

labelFontSize geometry()
property documented

  Previous releases did not document the `labelFontSize` property returned by the `geometry()` method. This release adds the missing documentation.

### Bug fixes

Initial selection
outline not drawn

  Previous versions would not draw outline for initially selected and open groups if `rolloutDuration` is was set to zero. This release fixes the issue.

Needless layout
recomputation

  Previous versions would needlessly recompute group layout in response to changing certain options, which might adversely impact performance. The 3.4.4 release fixes the issue.

## Version 3.4.3

released on 11.07.2016

The 3.4.3 provides a number of minor bug fixes and improvements.

The 3.4.3 release is a drop-in replacement for the earlier 3.4.x releases.

### Improvements

Needless recomputation
of rectangular layouts

  Earlier 3.4.x releases would needlessly recompute the rectangular group layouts, which might impact the performance when visualizing very large data sets. The 3.4.3 release fixes the issue.

Incorrect title bar position

  Earlier 3.4.x releases might incorrectly position the title bar and obscure the group being hovered over. This would only happen for rectangular layouts with `stacking` set to `flattened` and `descriptionGroupPosition` equal to `bottom`. The 3.4.3 release fixes title bar positioning in this case.

Label-too-small dots
also drawn for
custom content

  As of the 3.4.3 release, the label-too-small dots will also be drawn when the `groupContentDecorator` is implemented. Drawing of label-too-small dots is controlled using the `groupLabelMinFontSize` option.

Documentation for label
bounding box properties

  Documentation of the geometry() method lacked the description of properties related to the bounding box of the group's label.

## Version 3.4.2

released on 13.11.2015

The 3.4.2 fixes a bug that prevented FoamTree demos from running when opened using the file:// protocol.

The 3.4.2 release is a drop-in replacement for the earlier 3.4.x releases.

### Bug fixes

file:// protocol in demos

  FoamTree 3.4.1 demos would fail when opened in the browser using file:// protocol. Version 3.4.2 fixes this issue.

## Version 3.4.1

released on 09.11.2015

The 3.4.1 provides a number of small bug fixes and improvements.

The 3.4.1 release is a drop-in replacement for the 3.4.0 release.

### Improvements

Consecutive new line chars
in group labels respected

  The 3.4.1 respects multiple consecutive new line characters (`\n`) in labels, so that you can create empty lines in labels.

Small labels in
high-resolution export

  When exporting visualization `imageData` with a high pixel ratio, FoamTree 3.4.1 will draw small-font labels rather than the "..." ellipsis.

### New demos

Interactive label editing

  Release 3.4.1 adds a demo with a simple utility for interactive label editing.

### Bug fixes

Certain groups may be
rendered transparent

  Releases prior to 3.4.1 may render certain groups transparent if `relaxationVisible` is set to `true` and `groupColorDecorator` is used. The 3.4.1 release fixes this issue.

Broken selection outline
on open & selected groups

  The 3.4.0 release may render broken selection outline when the selected group is open and its children are hovered over. The 3.4.1 release fixes this issue.

Container element's
position CSS property
overwriten

  If FoamTree container element was positioned (had the CSS `position` property different from `static`), the `position` property would get reset after calling `resize`. The 3.4.1 release fixes this issue.

## Version 3.4.0

released on 11.09.2015

The 3.4.0 release introduces the "flattened" view of the group hierarchy, a number of enhancements to custom group content and label drawing, performance improvements, bug fixes and new mini-app demos.

The 3.4.0 release is a drop-in replacement for the 3.3.x releases.

### New features

"Flattened" hierarchy view
  As of version 3.4.0 FoamTree can present the hierarchy in an alternative "flattened" view, shown on the left of the screen shot below. As opposed to the default hierarchical view, on the right in the screen shot, the "flattened" view shows all levels of the hierarchy at once. For more details, please see the `stacking` option documentation section and the Unit test coverage, FT500 explorer and SCADA dashboard demos.

### New demos

FT500 explorer

  The FT500 visualizes the ranking of world's 500 largest companies according to Financial Times. The demo relies the newly introduced flattened view view of the group hierarchy.

Photo explorer

  The Photo explorer demo visualizes Flickr's top daily photos. It uses the newly introduced `polygonContext` property of `groupContentDecorator` to fill FoamTree polygons with bitmap images.

SCADA sensor dashboard

  The SCADA dashboard demo uses to FoamTree to implement a dashboard of readouts from SCADA system sensors. The demo relies the newly introduced flattened view of the group hierarchy.

Fancy background
generator

  Not very useful on its own, the Background demo shows how to use FoamTree to generate procedural and animated backgrounds for various kinds of content.

Context menu demo

  The context menu demo shows how to implement group-specific context menus in FoamTree.

CodePen resources
  We have just started our CodePen profile, which will contain some FoamTree demos to fork and experiment with as well as some short FoamTree API recipes that we create outside of the main FoamTree release cycle.

### Improvements

Line breaks in labels
  You can now force line breaks in group labels, see the list of special characters supported in labels.
More control in
custom content drawing
  As of version 3.4.0, you can take complete control of how the group's polygon is drawn using the `polygonContext` property of `groupContentDecorator`. See the Photo explorer and SCADA dashboard demos for real-world applications of this property.
Getting group's neighbors
  You can now retrieve the neighbors of a group as part of the `geometry` information; the Background demo shows an example usage.
Attribution color theme
  You can now use the `attributionTheme` option to draw the attribution group in a light or dark color, works also in the demo binaries.
Better incremental drawing
  The 3.4.0 release makes incremental drawing more intelligent and slightly faster. You can further tune it using the `incrementalDraw` option.
Nicer rounded corners
  The 3.4.0 release improves the rendering of rounded corners at lower levels of deep hierarchies.

### Bug fixes

Custom drawing context

  The custom drawing context did not support reading of properties, such as `lineWidth`. Additionally, the custom drawing context would not correctly handle the `drawImage` method in high zoom levels.

  Version 3.4.0 fixes both issues.

Title bar when shown
maxLabelSizeForTitleBar=0
  Previous versions would show the title bar even when the `maxLabelSizeForTitleBar` was set to `0`. Version 3.4.0 fixes the issue and does not show the title bar in such a setting.
Incorrect auto label color
  Occasionally, FoamTree would fail to update the label color in response to the change in the group color, which may have resulted in low label-to-polygon contrast. The 3.4.0 release fixes the issue.
Infinite loop for zero
groupLabelVerticalPadding
  Previous releases might enter an infinite loop when the `groupLabelVerticalPadding` option was set to `0`. The 3.4.0 release fixes the issue.
initializer alias for
relaxationInitializer
does not work

  After renaming the `initializer` option to `relaxationInitializer`, the 3.3.x and 3.4.x releases keep `initializer` as an alias to `relaxationInitializer`. Versions 3.3.0, 3.3.1, and 3.3.2, however, did not respect the alias. The 3.4.0 release fixes this issue.

## Version 3.3.2

released on 03.04.2015

The 3.3.2 release fixes a number of minor issues found in version 3.3.1 and adds the 1.0.x legacy version emulation demo.

The 3.3.2 release is a drop-in replacement for the earlier 3.3.x releases.

### Improvements

Incremental drawing
improvements

  The 3.3.2 release replaces needless full visualization redraws with incremental ones. Also, the 3.3.2 release fixes possible drawing artifacts when `groupSelectionOutlineWidth` is larger than `groupBorderWidth`.

### Bug fixes

JavaScript error when
visualization container
is extremely small

  For extremely small visualization containers (below 60x60 pixels in size), FoamTree may throw a JavaScript error when the user hovers the mouse pointer over some groups. The 3.3.2 release fixes this issue.

Child groups may
disappear on resize

  In versions prior to 3.3.2 child groups of small parent groups may disappear on visualization area resize. The 3.3.2 release fixes this issue.

Ellipsis may appear
for blank labels

  Versions prior to 3.3.2 would attempt to draw a label even if it is an empty string. As a result, ellipsis might appear in some of the smaller groups with empty labels. The 3.3.2 release fixes this issue.

### New demos

FoamTree 1.0.x emulation

  Release 3.3.2 adds demo with emulation of the Flash-based 1.0.x line of FoamTree look and feel.

## Version 3.3.1

released on 19.03.2015

The 3.3.1 release fixes a number of minor issues found in version 3.3.0.

The 3.3.1 release is a drop-in replacement for the 3.3.0 release.

### Improvements

attributionWeight

  Version 3.3.1 introduces the `attributionWeight` option that you can use to influence the size of the attribution group relative to other groups.

attributionLogoScale

  Version 3.3.1 introduces the `attributionLogoScale` option that you can use to customize the scale at which FoamTree will draw the custom attribution logo.

attributionLogo accepts
the Image object

  Since version 3.3.1, the `attributionLogo` option also accepts the `Image` instances. With this enhancement, you can render an SVG attribution logo that will look well regardless of the zoom level.

attribution flag
passed to decorators

  As of version 3.3.1, the decorator functions, such as the `groupColorDecorator`, will receive an extra flag indicating if the currently processed group is an attribution group. Please see the `hierarchy` documentation for details.

### Bug fixes

attributionPosition
not respected for
rectangular layouts

  Version 3.3.0 did not respect the `attributionPosition` option when layout was set to `ordered` or `squarified`. Version 3.3.1 fixes this issue. Please note, however, that the position of the attribution site in rectangular layouts is limited to the top-left or bottom-right corner. Please see the `attributionPosition` documentation for more details.

## Version 3.3.0

released on 16.02.2015

The 3.3.0 release introduces rectangular group layouts as well as a number of small improvements and bug fixes.

The 3.3.0 release is a drop-in replacement for earlier 3.2.x releases.

### New features

Rectangular tree maps
  As of version 3.3.0 FoamTree can generate the traditional rectangular tree maps in addition to the default polygonal layouts. For more details, please see the `layout` option documentation section and the source code of the above demo.
Laying out groups
in left-to-right order

  Version 3.3.0 comes with support for laying out groups in the left-to-right fashion according to their original order in the data object. Such ordered layout is possible with both the polygonal and rectangular treemaps.

  To generate the ordered polygonal layout, set the `relaxationInitializer` to `ordered`; to generate the ordered rectangular layout, set `layout` to `ordered`. In both cases, set `layoutByWeightOrder` to `false`. Please see the ordered layouts demo source code for a complete example.

### Bug fixes

indexByWeight property
uninitialized for
lower level groups

  Versions 3.2.x leave the `indexByWeight` property, available for decorator functions and from the `hierarchy` option, uninitialized for non-root level groups. Version 3.3.0 fixes this issue.

### API changes

initializer renamed to
relaxationInitializer

  As of version 3.3.3, the `initializer` option would only be applicable when `layout` is set to `relaxed`. To emphasize this dependency, the `initializer` option has been renamed to `relaxationInitializer`. With this change, all options that apply only when `layout` is `relaxed` have the `relaxation*` prefix.

  The 3.3.x and 3.4.x versions will keep `initializer` as an alias for `relaxationInitializer`. In 3.5.0, the `initializer` alias will be removed.

relaxationInitializer
value changes
  To better accommodate the multiple initializer algorithms, some values of the `relaxationInitializer` option have been renamed as follows:

  Old value | New value
  --- | ---
  `treemap` | becomes `squarified`
  `order` | becomes `ordered`

  The 3.3.x and 3.4.x versions will keep the previous values as aliases. In 3.5.0 the aliases will be removed.

## Version 3.2.3

released on 12.11.2014

The 3.2.2 release fixes a number of minor issues found in the earlier versions.

The 3.2.3 release is a drop-in replacement for earlier 3.2.x releases.

### Improvements

Getting current zoom scale
  As of version 3.2.3, you can get the `viewport` property to access the current zoom scale and offsets. This information may be useful when drawing custom content on a separate canvas on top of FoamTree.

### Bug fixes

Preventing default action
for onGroupDragStart
should prevent panning

  Earlier versions required preventing the default action of all `onGroupDragStart`, `onGroupDrag` and `onGroupDragEnd` to reliably disable panning of the visualization area. As of version 3.2.3, it is enough to prevent the default action of `onGroupDragStart` to achieve the same effect. Please see the `viewport` option for a practical example.

Incorrect gesture in the
interaction hints utility

  The interaction guide utility incorrectly contained the right double-click as the gesture to close the group. This release corrects the name of the gesture to be right click-and-hold.

JavaScript errors
on unsupported browsers

  Previous versions of FoamTree, when loaded on unsupported browsers, such as Internet Explorer 7, would throw JavaScript errors before the the calling code had a chance to inspect the `supported` property. Version 3.2.3 fixes this issue.

## Version 3.2.2

released on 10.11.2014

The 3.2.2 release fixes a number of minor issues found in the earlier versions and adds the possibility to control the style, weight and variant of labels.

The 3.2.2 release is a drop-in replacement for earlier 3.2.x releases.

### Improvements

Font style, weight and
variant customization
  As of version 3.2.2, you can set the style, weight and variant of the font used to draw group labels, title bar and generic text. For details, see: `groupLabelFontStyle`, `groupLabelFontWeight`, `groupLabelFontVariant`, `titleBarFontStyle`, `titleBarFontWeight`, `titleBarFontVariant` and `context.fillPolygonWithText`.

HTML tooltips example
  Version 3.2.2 comes with an example of displaying rich HTML-based tooltips for each group.

More precise
group hover detection
  Version 3.2.2 takes into account the `groupBorderWidth` when reporting group hover change in the `onGroupHover` event. As a result, if the user pointer moves to the gap between groups, FoamTree will trigger `onGroupHover` event with an `undefined` group to indicate that no group is being hovered over.

### Bug fixes

JavaScript errors when
showZeroWeightGroups
= false

  Versions prior to 3.2.2 may throw JavaScript errors when some groups have zero weight and `showZeroWeightGroups` is set to `false`.

  Version 3.2.2 fixes this issue.

## Version 3.2.1

released on 09.09.2014

The 3.2.1 release fixes a number of minor issues found in the earlier versions and adds the possibility to control the position of the attribution group.

The 3.2.1 release is a drop-in replacement for earlier 3.2.x releases.

### Improvements

Setting position of the
attribution group
  As of version 3.2.1, you can set the desired position of the attribution group using the `attributionPosition` and `attributionDistanceFromCenter` attributes.

Lazy loading of data
  Example code of lazy-loading of visualization data has been added.

### Bug fixes

Touch-enabled desktop
devices supported

  Versions prior to 3.2.1 would disable mouse- and keyboard-based interactions when support for touch input was detected. This would make it impossible to interact with FoamTree using keyboard and mouse on a regular desktop computer with a touch-enabled monitor attached.

  Version 3.2.1 fixes this issue.

Correctly closing
unexposed groups

  Versions prior to 3.2.1 would close an unexposed group only when he user right-double-clicked some child group of the exposed group. If the user right-double-clicked outside of the exposed group, the group would get unexposed, but it would remain open.

  As of version 3.2.1, if a group was opened as part the expose interaction, it will get closed when the group gets unexposed, regardless of the gesture that triggered the unexposure.

groupContentDecorator
correctly set after
FoamTree is initialized
  Versions 3.1.0 and 3.2.0 would ignore the `groupContentDecorator` if set after FoamTree was initialized. The 3.2.1 release fixes this issue.

JavaScript error in
groupContentDecorator
at high zoom levels
  Version 3.2.0 may throw JavaScript errors inside `groupContentDecorator` code when drawing content at very high zoom levels. Version 3.2.1 fixes the issue.

## Version 3.2.0

released on 08.07.2014

The 3.2.0 release makes it possible to browse very large hierarchies consisting of 100k+ groups at 100+ levels. It also introduces a number of minor related improvements and bug fixes.

The 3.2.0 release is a drop-in replacement for earlier 3.1.x releases in terms of API compatibility. For a list of functional changes, please see below.

### New features

Support for browsing
very large data sets
  As of version 3.2.0, FoamTree switches from eagerly precomputing the layout for all group levels to lazily initializing the layout when required. As a result of this change, you can now use FoamTree to browse hierarchies consisting of many thousands of nodes without noticeable performance degradation. For more details, please see the Visualizing large data sets section and the source code of the above demo.

### Improvements

Programmatic zooming
  You can now use the `zoom` method to zoom to the specified group.
Performance improvements

  Version 3.2.0 significantly improves performance when `rolloutDuration` is `0`. This setting is particularly useful when visualizing large numbers of groups on one level.

  Also, you can use the `maxGroupLevelsDrawn` and `maxGroupLabelLevelsDrawn` options to improve the rendering performance at the cost of lowering the amount of detail.

### Functional changes

Not all levels
of groups drawn

  As of version 3.2.0, FoamTree draws only `maxGroupLevelsDrawn` of closed group levels. In some cases, such as lowered `parentFillOpacity`, you may want to increase the `maxGroupLevelsDrawn` to bring more group levels into view.

## Version 3.1.0

released on 03.06.2014

The 3.1.0 release adds two major new features: drawing custom content in FoamTree polygons and the ability to dynamically update the weights of existing groups. Additionally, version 3.1.0 comes with a number of new demos, minor improvements and bug fixes.

The 3.1.0 release is a drop-in replacement for earlier 3.0.x releases.

### New features

Drawing custom content
in FoamTree polygons
  Since version 3.1.0, the `groupContentDecorator` option makes it possible to draw arbitrary content in each FoamTree polygon. Please see the examples in the documentation as well as the SVG images and GitHub search for example decorations that can be drawn.
Dynamic weight updates
  It is now possible to dynamically update weights of the visualized groups, FoamTree will perform an animated transition between the old and new weight values. See the `update` method for a complete example.
Data generator
in settings panel
  The settings panel now comes with a data generator that you can use to test your settings with data sets of different characteristics.
New demos
  Version 3.1.0 come with four new demos: GitHub search, CSV to FoamTree, Code coverage visualization and Images in polygons. All of the demos make advanced use of the new and existing FoamTree APIs.

### Improvements

Per-group layout initializers
  You can now specify different layout initializers for each parent group separately.
Redraw hints
  You can now suggest which groups need redrawing when calling the `redraw` method.
Zoom scale exposed
in titleBarDecorator
  Current zoom scale is now provided in the titleBarDecorator, which may be useful to determine the title bar visibility when the content of the polygon is customized using the `groupContentDecorator`.

### Bug fixes

Fixing a memory leak when
groupGrowingDuration > 0

  When `groupGrowingDuration` was larger than `0`, FoamTree would prevent the objects provided in the `dataObject` option from being garbage collected even after some new data was subsequently set for rendering. Please note that the default value of `groupGrowingDuration` is `0`, so this issue does not affect set ups running with the default value of that option.

  The 3.1.0 release fixes the memory leak.

Undocumented options

  Since version 3.0.0, the `imageData` offered the `backgroundColor` option that may come especially useful when exporting JPEG images. The option was undocumented in previous versions.

  Also, the `order` `relaxationInitializer` option was previously undocumented.

### API changes

groupLabelDottingThreshold
removed

  Version 3.1.0 removes the `groupLabelDottingThreshold` option. FoamTree will now always attempt to fit the group's label into its polygon and will draw the ellipsis instead of the label if the label's font size (taking the current zoom factor into account) is smaller than `groupLabelMinFontSize`.

## Version 3.0.3

released on 10.04.2014

The 3.0.3 release fixes a number of rendering issues found in version 3.0.2.

The 3.0.3 release is a drop-in replacement for earlier 3.0.x releases.

### Improvements

Better rendering
of small-font labels
  Version 3.0.3 improves rendering of small-font labels of deeply-nested groups in Chrome, Safari and Firefox. The image below shows small-font label rendered in Chrome by FoamTree 3.0.2 (left) and FoamTree 3.0.3 (right).

Allowing default browser
actions on selected events
  Version 3.0.3 makes it possible to allow default browser actions on selected events, such as mouse wheel movement. To allow the default browser event action, call the allowOriginalEventDefault() method on the appropriate interaction event details object. The scrollable content example shows how to use this technique to allow page content scrolling when mouse wheel is moved over FoamTree element.

### Bug fixes

Incorrect handling
of numeric group
identifier `0`
  Fixes a problem with groups identified with an integer `0`, which could not be selected and were ignored in methods accepting a list of identifiers.

## Version 3.0.2

released on 14.02.2014

The 3.0.2 release fixes a number of rendering issues found in version 3.0.1 and provides example code that emulates the look and feel of FoamTree 2.0.x.

The 3.0.1 release is a drop-in replacement for version 3.0.1 and 3.0.0.

### Improvements

FoamTree 2.0.x emulation
  Version 3.0.2 adds a code example that emulates the look and feel of FoamTree 2.0.x. Please note that the example code requires FoamTree 3.0.2 or later to run (FOAMTREE-201).
FOAMTREE-203
  As of version 3.0.2, the `geometry` option exposes the coordinates of the top-left corner of the group polygon's bounding box.
FOAMTREE-209
  Version 3.0.2 reintroduces the `indexByWeight` group property known from FoamTree 2.0.x. The `indexByWeight` property is available in the `groupColorDecorator` callback and also directly through the `hierarchy` read-only option.

### Bug fixes

Certain options ignored
  Versions 3.0.0 and 3.0.1 would ignore certain options when provided at `CarrotSearchFoamTree` instantiation. When the same options were changed using the `set` method, the changes would not be ignored. The following options were affected by this bug: `groupLabelFontFamily`, `groupLabelLineHeight`, `groupLabelHorizontalPadding`, `groupLabelVerticalPadding`, `groupLabelMaxTotalHeight`, `groupLabelMaxFontSize`. FoamTree 3.0.2 fixes the issue (FOAMTREE-211).
Incorrect polygon clipping
  Versions 3.0.0 and 3.0.1 would occasionally incorrectly clip the child groups' polygons, which may have resulted in distorted rendering and "flickering" during animated relaxation. Version 3.0.2 fixes the issue and also slightly improves the performance of polygon rendering (FOAMTREE-207, FOAMTREE-210).
All-black child groups
  If a group is the only direct child group of some parent, versions 3.0.0 and 3.0.1 would render the child group in black, regardless of the lightness and color of the parent group. Version 3.0.2 fixes the issue (FOAMTREE-202).
Non-string group IDs
  Versions prior to 3.0.2, did not resolve non-string group identifiers used in individual and multiple group selectors. Version 3.0.2 fixes this issue (FOAMTREE-206).

## Version 3.0.1

released on 09.01.2014

The 3.0.1 release fixes a number of minor issues found in version 3.0.0.

The 3.0.1 release is a drop-in replacement for version 3.0.0.

### Improvements

FOAMTREE-199
  Version 3.0.1 improves rendering performance estimation on Webkit-based browsers for increased animation smoothness.
FOAMTREE-200
  Version 3.0.1 improves the layout of demo pages viewed on mobile devices.

### Bug fixes

FOAMTREE-197
  Version 3.0.0 incorrectly computed touch event coordinates when Hammer.js was used to detect touch events. Version 3.0.1 fixes this issue and additionally improves the performance of event detection on both desktop and mobile devices.
FOAMTREE-198
  Version 3.0.0 would throw a JavaScript error on browsers that support local storage in general but deny the use of it to the specific page in which FoamTree is embedded. Version 3.0.1 fixes this issue.

## Version 3.0.0

released on 07.01.2014

FoamTree 3.0.0 is a major new release with the layout and display engine rebuilt from ground up and a number of API improvements.

### New features

New layout engine
  FoamTree 3.0.0 comes with a completely new layout and display engine. The new engine allows the same data to be displayed in many arrangements (such as with large groups in the center, small groups in the center or large groups in the corner, like in FoamTree 2.0.x) while preserving the group weight to polygon area relationship.
Zooming & panning
  The new display engine allows you to zoom into specific groups and pan around the visualization. On touch devices, the natural scaling and panning gestures are supported.
Exposure
  Double clicking on a group, zooms to the group, visually highlights it and opens for browsing the child groups.
New label fitting
  The new label fitting algorithm is much faster and better uses the available polygon area. The new algorithm also supports special characters, such as non-breakable space, zero-width space and soft-hyphens.
Polygon decorations
  New polygon decorations are now available, including stroke and drop shadow.
New effects
  Using detailed settings, you can now create your own rollout and pullback effects.
Utility scripts
  FoamTree now comes with a number of utility scripts which you can use to add data loading progress indicator or a simple interaction guide dialog.
API improvements
  Version 3.0.0 introduces a number of API improvements, such as registering multiple listener functions for the same event or promises.

### Migration

For typical use cases of the FoamTree API, version 3.0.0 should be a drop-in replacement for FoamTree 2.0.x. The most common adjustment, if any, would be updating your code not to use the options deprecated in version 2.0.x that have been removed in version 3.0.0. To quickly identify deprecated options in your code, run FoamTree with debugging assertions right after the update. You may also consider using some of the API improvements added in version 3.0.0.

If you have any problems porting your code to version 3.0.0, feel free to contact us for help.

Heads up!

While FoamTree 3.0.x is a drop-in replacement for the 2.0.x line in terms of the JavaScript API compatibility, it introduced a number of option defaults changes to refresh the visual appearance and improve interaction.

If you'd like to revert to the look and feel known from FoamTree 2.0.x, you can do so by tweaking a number of options as this example code shows. The example is available in FoamTree 3.0.2 and later.

### API changes

#### Noteworthy improvements

multiple event listeners
  You can now register multiple event listeners for the same type of event. Furthermore, you can now use the `on` and `off` methods to add and remove event listeners without affecting the other registered listeners.
promises
  Groups can now be open and exposed using the `open` and `expose` methods. The methods return promises that get resolved after the associated animation completes.
group properties
  You can now query various properties related to the group's state by reading the `state`, `hierarchy` and `geometry` options.
new group selectors
  There are more ways to designate a group or groups to be selected, open or exposed. In particular, the data object representing the group can be used as a selector now.
preventing default actions
  You can now prevent the default interactions by calling the `preventDefault()` method of the low-level event object in the appropriate low-level event, such as `onGroupClick`.

#### Deprecations

`onGroupSelectionChanged`
  The array of selected groups previously available in the `selectedGroups` property of the `onGroupSelectionChanged` event details is now available as the `groups` property.
`groupLabelDecorator`
  FoamTree 2.0.x would expose the `labelColor` variable both in `groupLabelDecorator` and `groupColorDecorator`. As of version 3.0.0, `labelColor` is only available in `groupColorDecorator`.

#### Renamed options

A number of options have been renamed to form consistent option-grouping prefixes or to follow conventions established by other option names.

`labelDarkColor`
  renamed to `groupLabelDarkColor`
`labelLightColor`
  renamed to `groupLabelLightColor`
`labelColorThreshold`
  renamed to `groupLabelColorThreshold`
`groupGradientRadius`
  renamed to `groupFillGradientRadius`
`groupOverlayOpacity`
  renamed to `parentFillOpacity`
`groupFontFamily`
  renamed to `groupLabelFontFamily`
`groupMaxTotalLabelHeight`
  renamed to `groupLabelMaxTotalHeight`
`minGroupDiameter`
  renamed to `groupMinDiameter`
`pixelRatioDuringRollout`
  renamed to `wireframePixelRatio`
`onGroupOpenOrClose`
  renamed to `onGroupOpenOrCloseChanged`

#### Removed options

A number of options have been removed. Certain removed options have been replaced with other options that allow you to achieve the same and a range of similar effects. A number of options have been removed because they were not relevant in the updated layout and rendering engine.

`backgroundColor`

  as of FoamTree 3.0.0 incremental group redrawing is performed on a much larger scale. Explicit drawing of background color would prevent FoamTree from incremental visualization updates and thus slow down rendering.

  You can modify the background of the visualization by applying CSS styling to the HTML element in which FoamTree is embedded .

`groupGradientType`
  use the `groupFillType` to `plain` to disable gradients.
`groupGradientStrength`
  use the `groupFillGradientCenterLightnessShift` and `groupFillGradientRimLightnessShift` to achieve similar effects.
`groupSelectionColor`
  use the `groupSelectionFillSaturationShift` and `groupColorDecorator` to tune the fill color of selected groups.
`groupHoverColor`
  use the `groupHoverFillSaturationShift` and `groupColorDecorator` to tune the fill color of hovered groups.
`groupHoverOutlineColor`
  use the `groupHoverStrokeSaturationShift` and `groupHoverStrokeLightnessShift` options to tune the stroke color of hovered groups.

`groupLinePadding`
  renamed to `groupLabelLineHeight`, which now behaves similar to the CSS `line-padding` property.
`groupMinFontSize`
  to `groupLabelMinFontSize`. Note: as of FoamTree 3.0.0 only pixel-based values are supported. You can apply percentage-based scaling using the `groupLabelMaxTotalHeight` option.
`groupMaxFontSize`
  to `groupLabelMaxFontSize`. Note: as of FoamTree 3.0.0 only pixel-based values are supported. You can apply percentage-based scaling using the `groupLabelMaxTotalHeight` option.

`rolloutType`
  you can now create your own rollout effects using the rollout options. Please see the API reference or the settings panel for a number of predefined effects.
`rolloutSpeed`
  use `rolloutDuration` to determine the duration of the initial animation.
`rolloutRate`
  use `rolloutLabelDrag` options to determine how quickly subsequent groups are included in the initial animation.
`siblingRevealingParallelism`
  you can use the `rolloutChildGroupsDrag` option to control the sibling revealing parallelism.
`childRevealingParallelism`
  you can use the `rolloutChildGroupsDelay` option to control the child group revealing parallelism.
`fadeInTime`
  fading-in time is now controlled by the overall `rolloutDuration` option. To use a simple fade-in effect instead of the animated rollout, see the `fadeDuration` option.
`fadeOutTime`
  fading-out time is now controlled by the overall `pullbackDuration` option. To use a simple fade-out effect instead of the animated pullback, see the `fadeDuration` option.
`drawLabelsDuringRollout`
  use the `wireframeLabelDrawing` option to control label drawing during animations.

`onBeforeSelection`
  you can now prevent the selection action directly in the `onGroupClick` event.
`onBeforeOpenOrClose`
  you can now prevent the group opening action directly in the `onGroupHold` event.
`roundBordersDuringRollout`
  with improved rendering speed, this option is not relevant any more.

`useFastRenderingRoutines`
  not relevant as of FoamTree 3.0.0
`aspectRatioImprovementSteps`
  not relevant as of FoamTree 3.0.0
`groupWeightScaling`
  not relevant as of FoamTree 3.0.0
`groupsVisibleAtRolloutStart`
  not relevant as of FoamTree 3.0.0
`explodeDistance`
  not relevant as of FoamTree 3.0.0

## Version 2.0.4

released on 04.09.2013

### Improvements

- Added support for `altKey`, `shiftKey` and other key modifiers to the object passed to all mouse or touch callback events.
- `onGroupClick` callback provides `group` property for the topmost group one clicked on.

### Bug fixes

- FoamTree cells may move around (in a cycle) on redraw.

## Version 2.0.3

released on 06.03.2013

### Improvements

- A number of events have been added to make it possible to change the default user interaction mappings, such as selecting groups on an event other than single mouse click.

### Bug fixes

- A JavaScript error would be thrown when setting or getting unknown option. Release 2.0.3 fixes this issue and logs the appropriate warning to the browser console.

## Version 2.0.2

released on 06.03.2013

### Improvements

- The `groupWeightScaling` option has been added to influence the relative sizes of group polygons.
- Versions prior to 2.0.2 would not draw selection outline when `groupBorderWidth` was smaller than 2. Version 2.0.2 draws selection outline for all group border widths.

### Bug fixes

- Pinch-in and -out gestures would not always open/close the desired group when the FoamTree HTML element was absolutely positioned. Release 2.0.2 fixes this issue.
- When a number of groups had the same weight, FoamTree might needlessly change the layout of polygons during redraw. As of release 2.0.2 the layout remains stable if the size of the visualization is not changing.
- The `onRedraw` callback would not be called after new data set was loaded. Release 2.0.2 fixes this problem.
- Options object passed to the `set` method could get modified by FoamTree. Release 2.0.2 will not modify objects passed to the `set` method.
- Options objects passed to the decorator callbacks, such as `groupColorDecorator` contained obfuscated option names. As of release 2.0.2, the callbacks will receive options object with original option names.
- Color variables in decorator callbacks, such as `groupColorDecorator`, now support CSS3 color strings in hex, hsl, hsla, rgb and rgba models in addition to the object-based color specification.
- On Mac OS, ⌘+click brings up context menu instead of adding new groups to the current selection. Release 2.0.2 fixes this issue.
- The `group` property was undefined in `groupLabelDecorator` for the attribution group. Release 2.0.2 returns the `{ attribution: true }` object in that property.

### Deprecations

- As of version 2.1.0, the property in which the `onGroupSelectionChanged` event exposes the selected groups will be renamed from `selectedGroups` to `groups`. Throughout 2.0.x line, the deprecated property `selectedGroups` will be maintained as an alias in addition to the new property.
- As of version 2.1.0, the `labelColor` variable affecting group label color will be moved from `groupLabelDecorator` to `groupColorDecorator`. To allow rapid migration and to preserve compatibility, the 2.0.x line will handle `labelColor` changes both in `groupLabelDecorator` and `groupColorDecorator`.

## Version 2.0.1

released on 08.08.2012

### Improvements

- The 2.0.1 release introduces support for high-density screens, such as Retina. You can use the `pixelRatio` option to set the physical-to-display pixel ratio FoamTree should assume.
- As of the 2.0.1 release, group `weight`s become optional in the `dataObject` specification. Groups with unspecified weights will be assumed to have the weight equal to `1.0`.

### Bug fixes

- Opening a group by double click would change the selection state of the group being open. Release 2.0.1 fixes this issue.
- Adding a CSS border to the FoamTree container element might create a blurry visualization image. Release 2.0.1 fixes this issue.
- FoamTree optimized rendering routines would produce incorrect results on certain mobile devices, most likely caused by the browser bugs. Until the bugs get fixed, as of version 2.0.1 FoamTree defaults to slower but correct rendering routines on those devices. Please see the `useFastRenderingRoutines` option for more details.
- In certain cases the initial layout generated by FoamTree might be unstable and change after the first call to `redraw`. Version 2.0.1 fixes the issue.
- FoamTree would not redraw groups without `id`s on programmatic changes of selection and expansion. Version 2.0.1 fixes the issue.
- FoamTree would throw a JavaScript error when selection is programmatically changed immediately after a new data model has been set. Version 2.0.1 fixes the issue.

## Version 2.0.0

released on 25.06.2012

Initial release of FoamTree for HTML5.