Highcharts plugins

Highcharts can be extended through plugins that allow us to add functionality without disturbing the core layer of code and are easy to share. There is a library of plugins available online contributed by Highcharts staff and other users at http://www.highcharts.com/plugin-registry. One distinct advantage of developing features out of plugins is that we can pick and choose the plugin features and build a compressed JavaScript library from them. In fact, we can already do something similar with the Highcharts library on the download page.

In this section, we will take a tour of a couple of plugins that you may find handy.

The regression plot plugin

When we create a scatter plot with lots of data points, it is often worthwhile to overlay them with a regression line. Of course, we can always achieve this by adding a line series manually. However, we still need to write the code for regression analysis. It is much more convenient to include a plugin. The Highcharts regression plugin created by Ignacio Vazquez does the job nicely. First, we include the plugin:

<script src="http://rawgithub.com/phpepe/highcharts-regression/master/highcharts-regression.js"> </script>

Then, we create our scatter chart as usual. Since we include the regression plugin, it provides additional regression options:

            series: [{
                regression: true ,
                regressionSettings: {
                    type: 'linear',
                    color:  'rgba(223, 83, 83, .9)'
                },
                name: 'Female',
                color: 'rgba(223, 83, 83, .5)',
                data: [ [161.2, 51.6], [167.5, 59.0],
                        .... 

Here is the chart from the demo (http://www.highcharts.com/plugin-registry/single/22/Highcharts%20regression):

The regression plot plugin

The draggable points plugin

Here is another remarkable plugin by Torstein that enables chart viewers to drag any series data points. We import the plugin with the following line:

<script src="http://rawgithub.com/highslide-software/draggable-points/master/draggable-points.js"></script>

This plugin brings two new point events, drag and drop, which we can define the handlers for via the plotOptions.series.point.events option (or the events option in a data point object). Here is the example code from the demo:

events: {                    
     drag: function(e) {
         // Update new data value in 'drag' div box
         $('#drag').html(
             'Dragging <b>' + this.series.name + '</b>, <b>' +
             this.category + '</b> to <b>' + 
             Highcharts.numberFormat(e.newY, 2) + '</b>'
         );
     },
     drop: function() {
         $('#drop').html(
              'In <b>' + this.series.name + '</b>, <b>' +
               this.category + '</b> was set to <b>' + 
               Highcharts.numberFormat(this.y, 2) + '</b>'
         );
     }
 }

When we select a data point and move the mouse, a drag event is triggered and the demo code will update the textbox below the chart, as seen in the following screenshot. The plugin provides several new options to control how we can drag and drop the data points. The following is a usage example:

    series: [{
        data: [0, 71.5, 106.4, .... ],
        draggableY: true,
        dragMinY: 0,
        type: 'column',
        minPointLength: 2
    }, {
        data: [0, 71.5, 106.4, ....],
        draggableY: true,
        dragMinY: 0,
        type: 'column',
        minPointLength: 2
    }, {

The Boolean option draggableX/Y notifies which direction the data points can be dragged in. Furthermore, the drag range can be limited by the dragMinX/Y and dragMaxX/Y options. The following screenshot shows a column being dragged:

The draggable points plugin

Creating a new effect by combining plugins

So far, we have seen the effect of two individual plugins. It's time for us to create a new user experience by loading these two plugins and combining their effects. The idea is to create a regression chart with movable data points, so that the regression line automatically adjusts in real time as we drag a data point. When doing so, we need to slightly modify the regression plugin code. Here is part of the original code:

(function (H) {
    
    H.wrap(H.Chart.prototype, 'init', function (proceed) {
       ....
       for (i = 0 ; i < series.length ; i++){
            var s = series[i];
            if ( s.regression && !s.rendered ) {
                 // Create regression series option
                 var extraSerie = {
                     ....
                 };
                 // Compute regression based on the type
                 if (regressionType == "linear") {
                    regression = _linear(s.data) ;
                    extraSerie.type = "line";
                 }
            }
      }
      // Append to series configuration array
      ....
   });
    
   function _linear(data) {
   ....
   }

   ....
})(Highcharts);

Basically, before the chart is created and rendered, the plugin scans the series data, computes the regression result, and formats the result into a line series option. To do that, the regression implementation is included as part of the init method for the Chart class, which is called when a Chart object is created. To extend an existing function in Highcharts, we call the wrap function on a method inside the object's prototype. In other words, when a Chart object is created, it will call the init function, which executes each function stacked internally (closure). We will further investigate this subject later.

For the purpose of updating the regression line at runtime, we need the ability to call _linear from outside the plugin. Here is a pseudo code of the new modification to add a new method, updateRegression:

(function (H) {    
    H.wrap(H.Chart.prototype, 'init', function (proceed) {
     ....
    });

    H.Chart.prototype.updateRegression = function(point) {
          // Get the series from the dragged data point
          var series = point.series;
          var chart = series.chart;
          // Get the regression series associated 
          // with this data series
          var regressSeries = chart.series[series.regressIdx];
           
          // Recompute based on the regression type and 
          // update the series
          if (series.regressionType == "linear") {
            regression = _linear(series.data) ;
          }
          regressSeries.update(regression, true);
    };

   function _linear(data) {
   ....
   }

   ....
})(Highcharts);

Now we have a regression plugin with an accessible method, updateRegression, to call the inner scope function _linear. With this new plugin function, we can link the functionality with the drag event exported by the draggable plugin:

plotOptions: {
  series: {
    point: {
      events: {
          drag: function(e) {
             // Pass the dragged data point to the     
             // regression plugin and update the
             // regression line
             Highcharts.charts[0]
                          .updateRegression(e.target);
        }
      }
    }
  }
}    

In order to observe the new effect more clearly, we use a smaller set of scatter plots. Here is the series configuration with both plugin options:

    series: [{
        regression: true ,
        regressionSettings: {
            type: 'linear',
            color:  'rgba(223, 83, 83, .9)'
        },
        draggableX: true,
        draggableY: true,
        name: 'Female',
        color: 'rgba(223, 83, 83, .5)',
        data: [ [161.2, 51.6], [167.5, 59.0], [159.5, 49.2], 
                [157.0, 63.0], [155.8, 53.6], [170.0, 59.0],
                [159.1, 47.6], [166.0, 69.8], [176.2, 66.8],
                ....

In the configuration, we have the scatter points draggable in both x and y directions and the regression type is linear. Let's load our new improved chart. The following is the initial screen:

Creating a new effect by combining plugins

Let's hypothetically assume an overactive, unapproved slim-fast drug, "mouse down", has slipped onto the market, which has some unreported side effects. The unfortunate ones will shoot up and the really unfortunate ones have their heights gravitate. Here is the outcome of the new result:

Creating a new effect by combining plugins

The regression line responds in real time as well as updating the top-left legend box as we mouse down those weights at the far-right data points.

Guidelines for creating a plugin

Some users create a plugin because certain tasks cannot be fulfilled by the API and the task is generic enough that it will be beneficial to other chart users. However, there is no standard API to create a plugin; developers have to be hands-on with their knowledge of Highcharts code. Nonetheless, there are a few guidelines that we can generalize from existing plugins.

Implementing the plugin within a self-invoking anonymous function

Always implement the plugin within a self-invoking anonymous function with Highcharts as the parameter. A self-invoking anonymous function is a pretty common technique in JavaScript. All the Highcharts plugins are implemented in this style. The following code shows an example:

(function (Highcharts) {
        ....
        // what happens in anonymous function, 
        // stays in anonymous function
        function hangoverIn(place) { 
           if (place === 'home') {
                return 'phew';
            } else if (place === 'hospital') {
                return 'ouch';
            } else if (place === 'vegas') {
                return 'aaahhhhh!!';
            }
        }
})(Highcharts);

None of the named functions and variables declared in the plugin are accessible externally because they are declared within the scope of a self-invoking anonymous function (closure and module pattern). Hence, the implementation is private to the outside world unless we assign properties in the Highcharts namespace.

Using Highcharts.wrap to extend existing functions

Depending on the plugin task, some plugins need to extend an existing function. For instance, the regression plugin calls H.wrap to extend the init function, which is called from the Chart constructor. See the following code:

(function (H) {
    H.wrap(H.Chart.prototype, 'init', 
        function (proceed) {
            // Plugin specific code for processing 
            // chart configuration
            .... 
            // Must include this instruction for wrap 
            // method to work
            proceed.apply(this,
                 Array.prototype.slice.call(arguments, 1));
        }
    );
})(Highcharts);

Highcharts.wrap is a commonly used function within plugins. The way wrap functions work is to overwrite the init function with a new function body that includes the previous implementation proceed. When we extend the method with our new plugin code in an anonymous function, we have to accept the proceed argument, which represents the parent function body. Before or after our plugin code, we must call proceed.apply on the same arguments in order to complete the chain of executions.

For reference, we can always extend methods for a particular series, for example, Highcharts.seriesTypes.column.prototype, where seriesTypes is an object containing all the series classes. Alternatively, if the plugin needs to be set up for all the series, we can invoke the wrap method on Highcharts.Series.prototype instead (all series classes are extended from Highcharts.Series).

Using a prototype to expose a plugin method

Sometimes we may need to export specific methods for a plugin. To do so, we should always define new methods inside the prototype property, such as:

(function (H) {
     H.Chart.prototype.pluginMethod = function(x, y) {
        ....            
     };
})(Highcharts);

This is because any code declared within the anonymous function is not accessible from the outside. Therefore, the only way to create a callable method is bound to the object passed to the anonymous function, which is the top level Highcharts object in this case. The prototype property is the standard way in JavaScript to inherit properties and methods from objects. The reason for attaching the method within the prototype property is because we don't know how developers will use the plugin. For instance, they may decide to create a new Chart object and call the plugin method. In such cases, the plugin code will still work.

Defining a new event handler

Another type of action for plugins is to define new events, as we saw in the draggable plugin. Here is the problem: we need access to a chart element to bind the event handler, that is, after the chart is rendered. However, the class init method is executed prior to the chart being rendered. This is where the Highcharts.Chart.prototype.callbacks array comes in. It is a place designed to store external functions that require access to elements. For example, the exporting module uses this array to insert buttons into the chart. Here is some pseudo code to set up events:

(function (H) {
    Highcharts.Chart.prototype.callbacks.push(function (chart) {
        // A DOM element, e.g. chart, container, document,
        var element = chart.container;
        // DOM event type: mousedown, touchstart, etc
        var eventName = 'mousedown';

        // We can call fireEvent to fire a new event 
        // defined in this plugin
        fireEvent(element, newEvent, evtArgs, 
                  function(args) {
                  ....
        });

        // Plugin event
        function handler(e) {
                // Plugin code
                ...
        }
        // Bind specific event to the defined handler
        Highcharts.addEvent(element, eventName, handler);
    });
})(Highcharts);

Highcharts has two event-related methods: addEvent and fireEvent. The former method is to bind an event to a chart element with a handler, whereas fireEvent triggers an event on an element.

The preceding code basically creates an anonymous function that organizes all the event(s) setup, such as defining the handler(s) and binding them to elements. The function must accept chart as the only parameter. Finally, we append the function into the callbacks array. The function will be automatically executed once the chart is fully rendered.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.128.205.21