From e11f69d5a29fd85d89bdf54d4ee68079acb960eb Mon Sep 17 00:00:00 2001
From: Daniel Cothran <daniel@andile.co>
Date: Thu, 7 Dec 2017 18:02:20 -0500
Subject: [PATCH] Issue: 2928419 by mwebaze: Charts Plugins

---
 charts.api.php                                |  37 +-
 charts.module                                 |  18 +-
 charts.services.yml                           |   3 +
 includes/charts.pages.inc                     |  30 +-
 .../src/Plugin/chart/CThreeCharts.php         | 116 ++++++
 .../src/Plugin/chart/GoogleCharts.php         | 369 ++++++++++++++++++
 .../src/Charts/HighchartsChartsRender.php     |  10 +-
 .../src/Plugin/chart/Highchart.php            | 154 ++++++++
 src/Annotation/Chart.php                      |  24 ++
 src/Plugin/chart/AbstractChart.php            |  34 ++
 src/Plugin/chart/ChartInterface.php           |  21 +
 src/Plugin/chart/ChartManager.php             |  30 ++
 12 files changed, 812 insertions(+), 34 deletions(-)
 create mode 100755 modules/charts_c3/src/Plugin/chart/CThreeCharts.php
 create mode 100755 modules/charts_google/src/Plugin/chart/GoogleCharts.php
 create mode 100755 modules/charts_highcharts/src/Plugin/chart/Highchart.php
 create mode 100755 src/Annotation/Chart.php
 create mode 100755 src/Plugin/chart/AbstractChart.php
 create mode 100755 src/Plugin/chart/ChartInterface.php
 create mode 100755 src/Plugin/chart/ChartManager.php

diff --git a/charts.api.php b/charts.api.php
index 8247270..cdd8c6a 100644
--- a/charts.api.php
+++ b/charts.api.php
@@ -50,15 +50,15 @@ use Drupal\charts\Theme\ChartsInterface;
  * chart_data, chart_xaxis, and chart_yaxis). For a full list, see the
  * charts_element_info() function.
  *
+ *
  * @see charts_element_info()
- */
-
-/**
+ *
+ *
  * Alter an individual chart before it is printed.
  *
- * @param array $chart
+ * @param $chart
  *   The chart renderable. Passed in by reference.
- * @param string $chart_id
+ * @param $chart_id
  *   The chart identifier, pulled from the $chart['#chart_id'] property (if
  *   any). Not all charts have a chart identifier.
  */
@@ -76,12 +76,9 @@ function hook_chart_alter(&$chart, $chart_id) {
  * name instead of being passed in as an argument.
  *
  * @see hook_chart_alter()
- *
- * @param array $chart
- *   Chart.
+ * @param $chart
  */
 function hook_chart_CHART_ID_alter(&$chart) {
-  
 }
 
 /**
@@ -96,14 +93,13 @@ function hook_chart_CHART_ID_alter(&$chart) {
  * Even though this hook may be fragile, it may provide developers with access
  * to library-specific functionality.
  *
- * @param array $definition
+ * @param $definition
  *   The chart definition to be modified. The raw values are passed directly to
  *   the charting library.
- * @param array $chart
+ * @param $chart
  *   The chart renderable. This may be used for reference (or read to add
  *   support for new properties), but any changes to this variable will not
  *   have an effect on output.
- *
  * @internal param $chart_id The chart ID, derived from the $chart['#chart_id'] property. Note that not*   The chart ID, derived from the $chart['#chart_id'] property. Note that not
  *   all charts may have a $chart_id.
  */
@@ -115,10 +111,8 @@ function hook_chart_definition_alter(&$definition, $chart) {
  *
  * Same as hook_chart_definition_alter(), only including the $chart_id in the
  * function name instead of being passed in as an argument.
- * @see hook_chart_definition_alter().
- *
- * @param array $chart
- *   Chart.
+ * @see hook_chart_definition_alter()
+ * @param $chart
  */
 function hook_chart_definition_CHART_ID_alter(&$chart) {
 }
@@ -149,9 +143,7 @@ function hook_charts_info() {
  *
  * If your module needs to modify the capabilities of a charting library, such
  * as to add support for a new chart type, it may do so with this hook.
- *
- * @param array $info
- *   Info.
+ * @param $info
  */
 function hook_charts_info_alter(&$info) {
   // Say the Google charts library supports geo charts.
@@ -175,8 +167,7 @@ function hook_charts_type_info() {
     // Many charting libraries always refer to the main axis as the "y-axis",
     // even if the chart's main axis is horizontal. An example of this is a
     // bar chart, where the values are along the horizontal axis.
-    // Meaning x/y axis are flipped.
-    'axis_inverted' => TRUE,
+    'axis_inverted' => TRUE, // Meaning x/y axis are flipped.
     // For bar/area/other charts that support stacking of series, set this value
     // to TRUE.
     'stacking' => TRUE,
@@ -189,9 +180,7 @@ function hook_charts_type_info() {
  *
  * If your module needs to modify the capabilities or labels of a paricular
  * chart type, it may alter the definitions provided by other modules.
- *
- * @param array $chart_types
- *   Chart Types.
+ * @param $chart_types
  */
 function hook_charts_type_info_alter(&$chart_types) {
   $chart_types['bar']['stacking'] = FALSE;
diff --git a/charts.module b/charts.module
index cce6439..86374a3 100644
--- a/charts.module
+++ b/charts.module
@@ -91,9 +91,19 @@ function template_preprocess_views_view_charts(&$variables) {
     $attachmentDisplayOptions[$i]['inherit_yaxis'] = $view->displayHandlers->get($attachmentId)->options['inherit_yaxis'];
   }
   $seriesData = array_merge($seriesData, $seriesDataAttachment);
-  $moduleSelector = new ModuleSelector($library, $categories, $seriesData, $options, $attachmentDisplayOptions, $chartId);
-  if ($moduleSelector->moduleExists()) {
-    $moduleSelector->buildVariables($variables);
-  }
 
+  $plugin_manager = \Drupal::service('plugin.manager.charts');
+  $plugin_definitions = $plugin_manager->getDefinitions();
+
+  if (!isset($plugin_definitions)) {
+      //to be removed in drupal 9
+      $moduleSelector = new ModuleSelector($library, $categories, $seriesData, $options, $attachmentDisplayOptions, $chartId);
+      if ($moduleSelector->moduleExists()) {
+          $moduleSelector->buildVariables($variables);
+      }
+  }
+  else{
+      $plugin = $plugin_manager->createInstance($library);
+      $plugin->buildVariables($options, $categories, $seriesData, $attachmentDisplayOptions, $variables, $chartId);
+  }
 }
diff --git a/charts.services.yml b/charts.services.yml
index 5320eb5..f769994 100644
--- a/charts.services.yml
+++ b/charts.services.yml
@@ -8,3 +8,6 @@ services:
   charts.settings:
     class: Drupal\charts\Services\ChartsSettingsService
     arguments: ['@config.factory']
+  plugin.manager.charts:
+    class: Drupal\charts\Plugin\chart\ChartManager
+    parent: default_plugin_manager
diff --git a/includes/charts.pages.inc b/includes/charts.pages.inc
index dfc80f3..1405b5e 100644
--- a/includes/charts.pages.inc
+++ b/includes/charts.pages.inc
@@ -198,8 +198,34 @@ function charts_settings_form($form, $defaults = [], $field_options = [], $paren
   // Ensure all defaults are set.
   $options = array_merge(charts_default_settings(), $defaults);
 
+  //using plugins to get the available installed libraries
+  $plugin_manager = \Drupal::service('plugin.manager.charts');
+  $plugin_definitions = $plugin_manager->getDefinitions();
+  $library_options = [];
+
+  foreach ($plugin_definitions as $plugin_definition){
+      $library_options[$plugin_definition['id']] = $plugin_definition['name'];
+  }
+
+  // Get a list of available chart libraries if plugins have not been implemented.
+    // This will be removed as ModuleSelector is now @deprecated
+
+  if (empty($library_options)){
+      $charts_info = charts_info();
+      $library_options = [];
+      foreach ($charts_info as $library_name => $library_info) {
+
+          if (Drupal::moduleHandler()->moduleExists($charts_info[$library_name]['module'])) {
+              $library_options[$library_name] = $library_info['label'];
+          }
+      }
+      if (empty($library_options)) {
+          drupal_set_message(t('There are no enabled charting libraries. Please enable a Charts sub-module.'));
+      }
+  }
+
   // Get a list of available chart libraries.
-  $charts_info = charts_info();
+  /*$charts_info = charts_info();
   $library_options = [];
   foreach ($charts_info as $library_name => $library_info) {
     if (Drupal::moduleHandler()->moduleExists($charts_info[$library_name]['module'])) {
@@ -208,7 +234,7 @@ function charts_settings_form($form, $defaults = [], $field_options = [], $paren
   }
   if (count($library_options) == 0) {
     drupal_set_message(t('There are no enabled charting libraries. Please enable a Charts sub-module.'));
-  }
+  }*/
   $form['library'] = [
     '#title' => t('Charting library'),
     '#type' => 'select',
diff --git a/modules/charts_c3/src/Plugin/chart/CThreeCharts.php b/modules/charts_c3/src/Plugin/chart/CThreeCharts.php
new file mode 100755
index 0000000..09e3ee0
--- /dev/null
+++ b/modules/charts_c3/src/Plugin/chart/CThreeCharts.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace Drupal\charts_c3\Plugin\chart;
+
+use Drupal\charts\Plugin\chart\AbstractChart;
+use Drupal\charts_c3\Settings\CThree\ChartType;
+use Drupal\charts_c3\Settings\CThree\CThree;
+use Drupal\charts_c3\Settings\CThree\ChartTitle;
+use Drupal\charts_c3\Settings\CThree\ChartData;
+use Drupal\charts_c3\Settings\CThree\ChartColor;
+use Drupal\charts_c3\Settings\CThree\ChartAxis;
+
+/**
+ * Define a concrete class for a Chart.
+ *
+ * @Chart(
+ *   id = "c3_charts",
+ *   name = @Translation("C3 Charts")
+ * )
+ */
+class CThreeCharts extends AbstractChart{
+    public function buildVariables($options, $categories = [], $seriesData = [], $attachmentDisplayOptions = [], &$variables,
+                           $chartId) {
+        $noAttachmentDisplays = count($attachmentDisplayOptions) === 0;
+        $types = [];
+
+        // @todo - make this work for more that one attachment.
+        for ($i = 1; $i <= count($attachmentDisplayOptions); $i++) {
+            if ($attachmentDisplayOptions[$i - 1]['style']['options']['type'] == 'column') {
+                $types[$seriesData[$i]['name']] = 'bar';
+            }
+            else {
+                $types[$seriesData[$i]['name']] = $attachmentDisplayOptions[$i - 1]['style']['options']['type'];
+            }
+        }
+        $c3Data = [];
+        for ($i = 0; $i < count($seriesData); $i++) {
+            $c3DataTemp = $seriesData[$i]['data'];
+            array_unshift($c3DataTemp, $seriesData[$i]['name']);
+            array_push($c3Data, $c3DataTemp);
+        }
+
+        $c3Chart = new ChartType();
+        $c3Chart->setType($options['type']);
+        $c3ChartTitle = new ChartTitle();
+        $c3ChartTitle->setText($options['title']);
+        $chartAxis = new ChartAxis();
+        $c3 = new CThree();
+        $bindTo = '#' . $chartId;
+        $c3->setBindTo($bindTo);
+        $c3->setTitle($c3ChartTitle);
+        $chartData = new ChartData();
+        if ($noAttachmentDisplays > 0) {
+            $chartData->setLabels(FALSE);
+        }
+
+        // Sets the primary y axis.
+        $yAxis = [];
+        $yAxis[$seriesData[0]['name']] = 'y';
+        $showAxis['show'] = TRUE;
+        $showAxis['label'] = $options['yaxis_title'];
+        $chartAxis->y = $showAxis;
+
+        // Sets secondary axis from the first attachment only.
+        if (!$noAttachmentDisplays && $attachmentDisplayOptions[0]['inherit_yaxis'] == 0) {
+            $yAxis[$seriesData[1]['name']] = 'y2';
+            $showSecAxis['show'] = TRUE;
+            $showSecAxis['label'] = $attachmentDisplayOptions[0]['style']['options']['yaxis_title'];
+            $chartAxis->y2 = $showSecAxis;
+        }
+
+        // Sets the chart type.
+        $chartData->setType($options['type']);
+        $c3->setData($chartData);
+        if ($options['type'] == 'bar') {
+            $chartAxis->setRotated(TRUE);
+            array_unshift($categories, 'x');
+            array_push($c3Data, $categories);
+            $chartData->setColumns($c3Data);
+        }
+        else if ($options['type'] == 'column') {
+            $chartData->setType('bar');
+            $chartAxis->setRotated(FALSE);
+            array_unshift($categories, 'x');
+            array_push($c3Data, $categories);
+            $chartData->setColumns($c3Data);
+        }
+        else if ($options['type'] == 'pie' || $options['type'] == 'donut') {
+            $chartData->setColumns($c3Data);
+        }
+        else {
+            array_unshift($categories, 'x');
+            array_push($c3Data, $categories);
+            $chartData->setColumns($c3Data);
+        }
+        $chartData->types = $types;
+
+        if ($options['type'] != 'pie' && $options['type'] != 'donut') {
+            $c3->setAxis($chartAxis);
+        }
+
+        $chartColor = new ChartColor();
+        $seriesColors = [];
+        for ($i = 0; $i < count($seriesData); $i++) {
+            $seriesColor = $seriesData[$i]['color'];
+            array_push($seriesColors, $seriesColor);
+        }
+        $chartColor->setPattern($seriesColors);
+        $c3->setColor($chartColor);
+
+        $variables['chart_type'] = 'c3';
+        $variables['content_attributes']['data-chart'][] = json_encode($c3);
+        $variables['attributes']['id'][0] = $chartId;
+        $variables['attributes']['class'][] = 'charts-c3';
+    }
+}
\ No newline at end of file
diff --git a/modules/charts_google/src/Plugin/chart/GoogleCharts.php b/modules/charts_google/src/Plugin/chart/GoogleCharts.php
new file mode 100755
index 0000000..a55d547
--- /dev/null
+++ b/modules/charts_google/src/Plugin/chart/GoogleCharts.php
@@ -0,0 +1,369 @@
+<?php
+namespace Drupal\charts_google\Plugin\chart;
+
+use Drupal\charts\Plugin\chart\AbstractChart;
+use Drupal\charts_google\Settings\Google\GoogleOptions;
+use Drupal\charts_google\Settings\Google\ChartType;
+use Drupal\charts_google\Settings\Google\ChartArea;
+use Drupal\charts_google\Settings\Google\HorizontalAxis;
+use Drupal\charts_google\Settings\Google\VerticalAxis;
+
+/**
+ * Define a concrete class for a Chart.
+ *
+ * @Chart(
+ *   id = "google",
+ *   name = @Translation("Google charts")
+ * )
+ */
+class GoogleCharts extends AbstractChart {
+    /**
+     * Creates a JSON Object formatted for Google charts to use
+     * @param array $categories
+     * @param array $seriesData
+     *
+     * @return json|string
+     */
+    public function buildVariables($options, $categories = [], $seriesData = [], $attachmentDisplayOptions = [], &$variables, $chartId) {
+        $categoriesCount = count($categories);
+        $seriesCount = count($seriesData);
+
+        // Creates an array of the length of the series data.
+        $dataCount = [];
+        for ($x = 0; $x < $seriesCount; $x++) {
+            $dataCountTemp = count($seriesData[$x]['data']);
+            array_push($dataCount, $dataCountTemp);
+        }
+
+        $dataTable = [];
+        for ($j = 0; $j < $categoriesCount; $j++) {
+            $rowDataTable = [];
+            for ($i = 0; $i < $seriesCount; $i++) {
+                $rowDataTabletemp = $seriesData[$i]['data'][$j];
+                array_push($rowDataTable, $rowDataTabletemp);
+            }
+            array_unshift($rowDataTable, $categories[$j]);
+            array_push($dataTable, $rowDataTable);
+        }
+
+        $dataTableHeader = [];
+        for ($r = 0; $r < $seriesCount; $r++) {
+            array_push($dataTableHeader, $seriesData[$r]['name']);
+        }
+        array_unshift($dataTableHeader, 'label');
+        array_unshift($dataTable, $dataTableHeader);
+
+        $googleOptions = $this->createChartsOptions($options, $seriesData, $attachmentDisplayOptions);
+        $googleChartType = $this->createChartType($options);
+        $variables['chart_type'] = 'google';
+        $variables['attributes']['class'][0] = 'charts-google';
+        $variables['attributes']['id'][0] = $chartId;
+        $variables['content_attributes']['data-chart'][] = json_encode($dataTable);
+        $variables['attributes']['google-options'][1] = json_encode($googleOptions);
+        $variables['attributes']['google-chart-type'][2] = json_encode($googleChartType);
+    }
+
+    /**
+     * Create charts options.
+     *
+     * @param array $options
+     *   Options.
+     * @param array $seriesData
+     *   Series data.
+     * @param array $attachmentDisplayOptions
+     *   Attachment Display Options.
+     *
+     * @return \Drupal\charts_google\Settings\Google\GoogleOptions
+     *   GoogleOptions object with chart options or settings to be used by google visualization framework.
+     */
+    private function createChartsOptions(array $options = [], array $seriesData = [], array $attachmentDisplayOptions = []) {
+        $noAttachmentDisplays = count($attachmentDisplayOptions) === 0;
+
+        $chartSelected = [];
+        $seriesTypes = [];
+
+        $firstVaxis = new VerticalAxis();
+
+        if (isset($options['yaxis_min'])) {
+            $firstVaxis->setMinValue($options['yaxis_min']);
+        }
+
+        if (isset($options['yaxis_view_min'])) {
+            $firstVaxis->setViewWindowValue('min', $options['yaxis_view_min']);
+        }
+
+        if (isset($options['yaxis_view_max'])) {
+            $firstVaxis->setViewWindowValue('max', $options['yaxis_view_max']);
+        }
+
+        if (isset($options['yaxis_max'])) {
+            $firstVaxis->setMaxValue($options['yaxis_max']);
+        }
+
+        // A format string for numeric or date axis labels.
+        if (isset($options['yaxis_title'])) {
+            $firstVaxis->setTitle($options['yaxis_title']);
+        }
+
+        if (isset($options['yaxis_title_color'])) {
+            $firstVaxis->setTitleTextStyleValue('color', $options['yaxis_title_color']);
+        }
+
+        if (isset($options['yaxis_title_font'])) {
+            $firstVaxis->setTitleTextStyleValue('fontName', $options['yaxis_title_font']);
+        }
+
+        if (isset($options['yaxis_title_size'])) {
+            $firstVaxis->setTitleTextStyleValue('fontSize', $options['yaxis_title_size']);
+        }
+
+        if (isset($options['yaxis_title_bold'])) {
+            $firstVaxis->setTitleTextStyleValue('bold', $options['yaxis_title_bold']);
+        }
+
+        if (isset($options['yaxis_title_italic'])) {
+            $firstVaxis->setTitleTextStyleValue('italic', $options['yaxis_title_italic']);
+        }
+
+        // Axis title position.
+        if (isset($options['yaxis_title_position'])) {
+            $firstVaxis->setTextPosition($options['yaxis_title_position']);
+        }
+
+        if (isset($options['yaxis_baseline'])) {
+            $firstVaxis->setBaseline($options['yaxis_baseline']);
+        }
+
+        if (isset($options['yaxis_baseline_color'])) {
+            $firstVaxis->setBaselineColor($options['yaxis_baseline_color']);
+        }
+
+        if (isset($options['yaxis_direction'])) {
+            $firstVaxis->setDirection($options['yaxis_direction']);
+        }
+
+        // A format string for numeric or date axis labels.
+        if (isset($options['yaxis_format'])) {
+            $firstVaxis->setFormat($options['yaxis_format']);
+        }
+
+        if (isset($options['yaxis_view_window_mode'])) {
+            $firstVaxis->setViewWindowMode($options['yaxis_view_window_mode']);
+        }
+
+        $firstHaxis = new HorizontalAxis();
+
+        if (isset($options['xaxis_min'])) {
+            $firstHaxis->setMinValue($options['xaxis_min']);
+        }
+
+        if (isset($options['xaxis_view_min'])) {
+            $firstHaxis->setViewWindowValue('min', $options['xaxis_view_min']);
+        }
+
+        if (isset($options['xaxis_view_max'])) {
+            $firstHaxis->setViewWindowValue('max', $options['xaxis_view_max']);
+        }
+
+        if (isset($options['xaxis_max'])) {
+            $firstHaxis->setMaxValue($options['xaxis_max']);
+        }
+
+        // A format string for numeric or date axis labels.
+        if (isset($options['xaxis_title'])) {
+            $firstHaxis->setTitle($options['xaxis_title']);
+        }
+
+        if (isset($options['xaxis_title_color'])) {
+            $firstHaxis->setTitleTextStyleValue('color', $options['xaxis_title_color']);
+        }
+
+        if (isset($options['xaxis_title_font'])) {
+            $firstHaxis->setTitleTextStyleValue('fontName', $options['xaxis_title_font']);
+        }
+
+        if (isset($options['xaxis_title_size'])) {
+            $firstHaxis->setTitleTextStyleValue('fontSize', $options['xaxis_title_size']);
+        }
+
+        if (isset($options['xaxis_title_bold'])) {
+            $firstHaxis->setTitleTextStyleValue('bold', $options['xaxis_title_bold']);
+        }
+
+        if (isset($options['xaxis_title_italic'])) {
+            $firstHaxis->setTitleTextStyleValue('italic', $options['xaxis_title_italic']);
+        }
+
+        // Axis title position.
+        if (isset($options['xaxis_title_position'])) {
+            $firstHaxis->setTextPosition($options['xaxis_title_position']);
+        }
+
+        if (isset($options['xaxis_baseline'])) {
+            $firstHaxis->setBaseline($options['xaxis_baseline']);
+        }
+
+        if (isset($options['xaxis_baseline_color'])) {
+            $firstHaxis->setBaselineColor($options['xaxis_baseline_color']);
+        }
+
+        if (isset($options['xaxis_direction'])) {
+            $firstHaxis->setDirection($options['xaxis_direction']);
+        }
+
+        // A format string for numeric or date axis labels.
+        if (isset($options['xaxis_format'])) {
+            $firstHaxis->setFormat($options['xaxis_format']);
+        }
+
+        if (isset($options['xaxis_view_window_mode'])) {
+            $firstHaxis->setViewWindowMode($options['xaxis_view_window_mode']);
+        }
+
+        $vAxes = [];
+        $hAxes = [];
+
+        array_push($vAxes, $firstVaxis);
+        array_push($hAxes, $firstHaxis);
+
+        // Sets secondary axis from the first attachment only.
+        if (!$noAttachmentDisplays && $attachmentDisplayOptions[0]['inherit_yaxis'] == 0) {
+            $secondVaxis = new VerticalAxis();
+            $secondVaxis->setTitle($attachmentDisplayOptions[0]['style']['options']['yaxis_title']);
+            array_push($vAxes, $secondVaxis);
+        }
+
+        array_push($chartSelected, $options['type']);
+
+        // @todo: make sure this works for more than one attachment.
+        for ($i = 0; $i < count($attachmentDisplayOptions); $i++) {
+            $attachmentChartType = $attachmentDisplayOptions[$i]['style']['options']['type'];
+
+            if ($attachmentChartType == 'column') {
+                $attachmentChartType = 'bars';
+            }
+
+            if ($attachmentDisplayOptions[$i]['inherit_yaxis'] == 0 && $i == 0) {
+                $seriesTypes[$i + 1] = [
+                    'type'            => $attachmentChartType,
+                    'targetAxisIndex' => 1
+                ];
+            }
+            else {
+                $seriesTypes[$i + 1] = ['type' => $attachmentChartType];
+            }
+
+            array_push($chartSelected, $attachmentChartType);
+        }
+
+        $chartSelected = array_unique($chartSelected);
+        $googleOptions = new GoogleOptions();
+
+        if (count($chartSelected) > 1) {
+            $parentChartType = $options['type'];
+
+            if ($parentChartType == 'column') {
+                $parentChartType = 'bars';
+            }
+
+            $googleOptions->seriesType = $parentChartType;
+            $googleOptions->series = $seriesTypes;
+        }
+
+        $googleOptions->setTitle($options['title']);
+
+        if (isset($options['subtitle'])) {
+            $googleOptions->setSubTitle($options['subtitle']);
+        }
+
+        $googleOptions->setVerticalAxes($vAxes);
+        $googleOptions->setHorizontalAxes($hAxes);
+
+        if (in_array('donut', $chartSelected)) {
+            $googleOptions->pieHole = '0.5';
+        }
+
+        $chartArea = new ChartArea();
+
+        // Chart Area width.
+        if (isset($options['chart_area']['width'])) {
+            $chartArea->setWidth($options['chart_area']['width']);
+        }
+
+        // Chart Area height.
+        if (isset($options['chart_area']['height'])) {
+            $chartArea->setHeight($options['chart_area']['height']);
+        }
+
+        // Chart Area padding top.
+        if (isset($options['chart_area']['top'])) {
+            $chartArea->setPaddingTop($options['chart_area']['top']);
+        }
+
+        // Chart Area padding left.
+        if (isset($options['chart_area']['left'])) {
+            $chartArea->setPaddingLeft($options['chart_area']['left']);
+        }
+
+        $seriesColors = [];
+        for ($i = 0; $i < count($seriesData); $i++) {
+            $seriesColor = $seriesData[$i]['color'];
+            array_push($seriesColors, $seriesColor);
+        }
+        $googleOptions->setColors($seriesColors);
+
+        // Width of the chart, in pixels.
+        if (isset($options['width'])) {
+            $googleOptions->setWidth($options['width']);
+        }
+
+        // Height of the chart, in pixels.
+        if (isset($options['height'])) {
+            $googleOptions->setHeight($options['height']);
+        }
+
+        // 'legend' can be a string (for position) or an array with legend
+        // properties: [position: 'top', textStyle: [color: 'blue', fontSize: 16]]
+        if (isset($options['legend'])) {
+            $googleOptions->setLegend($options['legend']);
+        }
+
+        // Set legend position.
+        if (isset($options['legend_position'])) {
+            if(empty($options['legend_position'])) {
+                $options['legend_position'] = 'none';
+                $googleOptions->setLegend($options['legend_position']);
+            } else {
+                $googleOptions->setLegend($options['legend_position']);
+            }
+        }
+
+        // Where to place the chart title, compared to the chart area.
+        if (isset($options['title_position'])) {
+            $googleOptions->setTitlePosition($options['title_position']);
+        }
+
+        // Where to place the axis titles, compared to the chart area
+        if (isset($options['axis_titles_position'])) {
+            $googleOptions->setAxisTitlesPosition($options['axis_titles_position']);
+        }
+
+        return $googleOptions;
+    }
+
+    /**
+     * Create Chart Type.
+     *
+     * @param array $options
+     *   Options.
+     *
+     * @return \Drupal\charts_google\Settings\Google\ChartType
+     *   ChartType.
+     */
+    private function createChartType(array $options = []) {
+        $googleChartType = new ChartType();
+        $googleChartType->setChartType($options['type']);
+
+        return $googleChartType;
+    }
+}
\ No newline at end of file
diff --git a/modules/charts_highcharts/src/Charts/HighchartsChartsRender.php b/modules/charts_highcharts/src/Charts/HighchartsChartsRender.php
index c70f023..7b522aa 100644
--- a/modules/charts_highcharts/src/Charts/HighchartsChartsRender.php
+++ b/modules/charts_highcharts/src/Charts/HighchartsChartsRender.php
@@ -49,10 +49,12 @@ class HighchartsChartsRender implements ChartsRenderInterface {
       }
       // Add innerSize to differentiate between donut and pie.
       foreach ($seriesData as $key => &$value) {
-        $innerSize['showInLegend'] = 'true';
-        $innerSize['innerSize'] = '40%';
-        $chartPlacement = array_search($value, $seriesData);
-        $seriesData[$chartPlacement] = array_merge($innerSize, $seriesData[$chartPlacement]);
+        if ($typeOptions == 'pie') {
+          $innerSize['showInLegend'] = 'true';
+          $innerSize['innerSize'] = '40%';
+          $chartPlacement = array_search($value, $seriesData);
+          $seriesData[$chartPlacement] = array_merge($innerSize, $seriesData[$chartPlacement]);
+        }
       }
     }
     $chart->setType($typeOptions);
diff --git a/modules/charts_highcharts/src/Plugin/chart/Highchart.php b/modules/charts_highcharts/src/Plugin/chart/Highchart.php
new file mode 100755
index 0000000..0cc1340
--- /dev/null
+++ b/modules/charts_highcharts/src/Plugin/chart/Highchart.php
@@ -0,0 +1,154 @@
+<?php
+namespace Drupal\charts_highcharts\Plugin\chart;
+
+use Drupal\charts\Plugin\chart\AbstractChart;
+//use Drupal\Charts\Annotation\Chart;
+use Drupal\charts_highcharts\Settings\Highcharts\Chart;
+//use Drupal\charts_highcharts\Settings\Highcharts\ChartType;
+use Drupal\charts_highcharts\Settings\Highcharts\ChartTitle;
+use Drupal\charts_highcharts\Settings\Highcharts\Xaxis;
+use Drupal\charts_highcharts\Settings\Highcharts\XaxisTitle;
+use Drupal\charts_highcharts\Settings\Highcharts\ChartLabel;
+use Drupal\charts_highcharts\Settings\Highcharts\YaxisLabel;
+use Drupal\charts_highcharts\Settings\Highcharts\Yaxis;
+use Drupal\charts_highcharts\Settings\Highcharts\YaxisTitle;
+use Drupal\charts_highcharts\Settings\Highcharts\DataLabelStatus;
+use Drupal\charts_highcharts\Settings\Highcharts\DataLabels;
+use Drupal\charts_highcharts\Settings\Highcharts\PlotOptions;
+use Drupal\charts_highcharts\Settings\Highcharts\Tooltip;
+use Drupal\charts_highcharts\Settings\Highcharts\ChartCredits;
+use Drupal\charts_highcharts\Settings\Highcharts\ChartLegend;
+use Drupal\charts_highcharts\Settings\Highcharts\Highcharts;
+/**
+ * Defines a concrete class for a Highcharts.
+ *
+ * @Chart(
+ *   id = "highcharts",
+ *   name = @Translation("Highcharts")
+ * )
+ */
+class Highchart extends AbstractChart{
+    /**
+     * Creates a JSON Object formatted for Highcharts to use
+     *
+     * @param $options
+     * @param array $categories
+     * @param array $seriesData
+     *
+     * @param array $attachmentDisplayOptions
+     *
+     * @return Highcharts object to be used by highcharts javascripts visualization framework
+     */
+    public function buildVariables($options, $categories = [], $seriesData = [], $attachmentDisplayOptions = [], &$variables, $chartId) {
+        $noAttachmentDisplays = count($attachmentDisplayOptions) === 0;
+
+        $chart = new Chart();
+        $typeOptions = $options['type'];
+        // @todo: make this so that it happens if any display uses donut.
+        if ($typeOptions == 'donut'){
+            $typeOptions = 'pie';
+            // Remove donut from seriesData.
+            foreach ($seriesData as $key => &$value) {
+                $value = str_replace('donut','pie',$value);
+            }
+            // Add innerSize to differentiate between donut and pie.
+            foreach ($seriesData as $key => &$value) {
+                $innerSize['showInLegend'] = 'true';
+                $innerSize['innerSize'] = '40%';
+                $chartPlacement = array_search($value, $seriesData);
+                $seriesData[$chartPlacement] = array_merge($innerSize, $seriesData[$chartPlacement]);
+            }
+        }
+        $chart->setType($typeOptions);
+
+        // Set chart width.
+        if (isset($options['width'])) {
+            $chart->setWidth($options['width']);
+        }
+
+        // Set chart height.
+        if (isset($options['height'])) {
+            $chart->setHeight($options['height']);
+        }
+
+        // Set chart title.
+        $chartTitle = new ChartTitle();
+        if (isset($options['title'])) {
+            $chartTitle->setText($options['title']);
+        }
+
+        $chartXaxis = new Xaxis();
+        $chartLabels = new ChartLabel();
+
+        // Set x-axis label rotation.
+        if (isset($options['xaxis_labels_rotation'])) {
+            $chartLabels->setRotation($options['xaxis_labels_rotation']);
+        }
+
+        $chartXaxis->setCategories($categories);
+
+        // Set x-axis title.
+        $xAxisTitle = new XaxisTitle();
+        if (isset($options['xaxis_title'])) {
+            $xAxisTitle->setText($options['xaxis_title']);
+        }
+        $chartXaxis->setTitle($xAxisTitle);
+        $chartXaxis->setLabels($chartLabels);
+        $yaxisLabels = new YaxisLabel();
+        $chartYaxis = new Yaxis();
+        $yAxes = [];
+        $yAxisTitle = new YaxisTitle();
+        $yAxisTitle->setText($options['yaxis_title']);
+        if (!empty($options['yaxis_min'])) {
+            $chartYaxis->min = $options['yaxis_min'];
+        }
+        if (!empty($options['yaxis_max'])) {
+            $chartYaxis->max = $options['yaxis_max'];
+        }
+
+        $chartYaxis->setLabels($yaxisLabels);
+        $chartYaxis->setTitle($yAxisTitle);
+        array_push($yAxes, $chartYaxis);
+
+        // Chart libraries tend to support only one secondary axis.
+        if (!$noAttachmentDisplays && $attachmentDisplayOptions[0]['inherit_yaxis'] == 0) {
+            $chartYaxisSecondary = new Yaxis();
+            $yAxisTitleSecondary = new YaxisTitle();
+            $yAxisTitleSecondary->setText($attachmentDisplayOptions[0]['style']['options']['yaxis_title']);
+            $chartYaxisSecondary->setTitle($yAxisTitleSecondary);
+            $chartYaxisSecondary->setLabels($yaxisLabels);
+            $chartYaxisSecondary->opposite = 'true';
+            if (!empty($attachmentDisplayOptions[0]['style']['options']['yaxis_min'])) {
+                $chartYaxisSecondary->min = $attachmentDisplayOptions[0]['style']['options']['yaxis_min'];
+            }
+            if (!empty($attachmentDisplayOptions[0]['style']['options']['yaxis_max'])) {
+                $chartYaxisSecondary->max = $attachmentDisplayOptions[0]['style']['options']['yaxis_max'];
+            }
+            array_push($yAxes, $chartYaxisSecondary);
+        }
+        $dataLabelStatus = new DataLabelStatus();
+        $dataLabels = new DataLabels();
+        $dataLabels->setDataLabels($dataLabelStatus);
+        $plotOptions = new PlotOptions();
+        $plotOptions->setPlotType($dataLabels);
+        $chartTooltip = new Tooltip();
+        $chartCredits = new ChartCredits();
+        $chartLegend = new ChartLegend();
+
+        $highchart = new Highcharts();
+        $highchart->setChart($chart);
+        $highchart->setTitle($chartTitle);
+        $highchart->setXAxis($chartXaxis);
+//    $highchart->yAxis = $yAxes;
+        $highchart->setTooltip($chartTooltip);
+        $highchart->setPlotOptions($plotOptions);
+        $highchart->setCredits($chartCredits);
+        $highchart->setLegend($chartLegend);
+        $highchart->setSeries($seriesData);
+
+        $variables['chart_type'] = 'highcharts';
+        $variables['content_attributes']['data-chart'][] = json_encode($highchart);
+        $variables['attributes']['id'][0] = $chartId;
+        $variables['attributes']['class'][] = 'charts-highchart';
+    }
+}
\ No newline at end of file
diff --git a/src/Annotation/Chart.php b/src/Annotation/Chart.php
new file mode 100755
index 0000000..2b95c0e
--- /dev/null
+++ b/src/Annotation/Chart.php
@@ -0,0 +1,24 @@
+<?php
+namespace Drupal\charts\Annotation;
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines an Chart annotation object.
+ *
+ * @Annotation
+ */
+class Chart extends Plugin{
+    /**
+     * The plugin ID.
+     *
+     * @var string
+     */
+    public $id;
+    /**
+     * The plugin name.
+     *
+     * @var string
+     */
+    public $name;
+
+}
\ No newline at end of file
diff --git a/src/Plugin/chart/AbstractChart.php b/src/Plugin/chart/AbstractChart.php
new file mode 100755
index 0000000..cc51fa0
--- /dev/null
+++ b/src/Plugin/chart/AbstractChart.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\charts\Plugin\chart;
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Base class Chart plugins.
+ */
+abstract class AbstractChart extends PluginBase implements ChartInterface, ContainerFactoryPluginInterface{
+    use StringTranslationTrait;
+    /**
+     * {@inheritdoc}
+     */
+    public function __construct(array $configuration, $plugin_id, $plugin_definition)
+    {
+        parent::__construct($configuration, $plugin_id, $plugin_definition);
+    }
+    /**
+     * {@inheritdoc}
+     */
+    public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition){
+        return new static(
+            $configuration,
+            $plugin_id,
+            $plugin_definition
+        );
+    }
+    public function getChartName() {
+        return $this->pluginDefinition['name'];
+    }
+}
\ No newline at end of file
diff --git a/src/Plugin/chart/ChartInterface.php b/src/Plugin/chart/ChartInterface.php
new file mode 100755
index 0000000..22d64be
--- /dev/null
+++ b/src/Plugin/chart/ChartInterface.php
@@ -0,0 +1,21 @@
+<?php
+namespace Drupal\charts\Plugin\chart;
+use Drupal\Component\Plugin\PluginInspectionInterface;
+/**
+ * Defines an interface for Chart plugins.
+ */
+interface ChartInterface extends PluginInspectionInterface{
+    /**
+     * Build Variables.
+     *
+     * @return an array
+     */
+    public function buildVariables($options, $categories, $seriesData, $attachmentDisplayOptions, &$variables, $chartId);
+    /**
+     * Return the name of the chart.
+     *
+     * @return string
+     *   returns the name as a string.
+     */
+    public function getChartName();
+}
\ No newline at end of file
diff --git a/src/Plugin/chart/ChartManager.php b/src/Plugin/chart/ChartManager.php
new file mode 100755
index 0000000..f3e4c4f
--- /dev/null
+++ b/src/Plugin/chart/ChartManager.php
@@ -0,0 +1,30 @@
+<?php
+namespace Drupal\charts\Plugin\chart;
+
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+
+/**
+ * @file
+ *
+ * Provides the Chart plugin plugin manager and manages discovery and instantiation of chart plugins.
+ *
+ */
+class ChartManager extends DefaultPluginManager{
+    /**
+     * Constructor for ChartManager objects.
+     *
+     * @param \Traversable $namespaces
+     *   An object that implements \Traversable which contains the root paths
+     *   keyed by the corresponding namespace to look for plugin implementations.
+     * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+     *   Cache backend instance to use.
+     * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+     *   The module handler to invoke the alter hook with.
+     */
+    public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler){
+        parent::__construct('Plugin/chart',$namespaces,$module_handler,'Drupal\charts\Plugin\chart\ChartInterface',
+            'Drupal\charts\Annotation\Chart');
+    }
+}
\ No newline at end of file
-- 
GitLab