You are here

charts.module in Charts 7.2

Provides elements for rendering charts and Views integration.

File

charts.module
View source
<?php

/**
 * @file
 * Provides elements for rendering charts and Views integration.
 */

/**
 * Constant used in hook_charts_type_info() to declare chart types with a single
 * axis. For example a pie chart only has a single dimension.
 */
define('CHARTS_SINGLE_AXIS', 'y_only');

/**
 * Constant used in hook_charts_type_info() to declare chart types with a dual
 * axes. Most charts use this type of data, meaning multiple categories each
 * have multiple values. This type of data is usually represented as a table.
 */
define('CHARTS_DUAL_AXIS', 'xy');

/**
 * Implements hook_menu().
 */
function charts_menu() {
  $items['admin/config/content/charts'] = array(
    'title' => 'Default chart configuration',
    'description' => 'Set the default library, behavior, and style of charts.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'charts_default_settings_form',
    ),
    'access arguments' => array(
      'administer charts',
    ),
    'file' => 'includes/charts.pages.inc',
  );
  $items['charts/examples'] = array(
    'title' => 'Charts examples',
    'page callback' => 'charts_examples',
    'access arguments' => array(
      'access example charts',
    ),
    'file' => 'includes/charts.examples.inc',
    'type' => MENU_NORMAL_ITEM,
  );
  $items['charts/examples/built-in'] = array(
    'title' => 'Built-in examples',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  return $items;
}

/**
 * Implements hook_element_info().
 */
function charts_element_info() {

  // Create the elements representing charts themselves.
  $info['chart'] = array(
    '#chart_type' => NULL,
    // Options: pie, bar, column, etc.
    '#chart_id' => NULL,
    // Machine name to alter this chart individually.
    '#title' => NULL,
    '#title_color' => '#000',
    '#title_font_weight' => 'normal',
    // Options: normal, bold
    '#title_font_style' => 'normal',
    // Options: normal, italic
    '#title_font_size' => 14,
    // Font size in pixels, e.g. 12.
    '#title_position' => 'out',
    // Options: in, out.
    '#colors' => charts_default_colors(),
    '#font' => 'Arial',
    '#font_size' => 12,
    // Font size in pixels, e.g. 12.
    '#background' => 'transparent',
    '#stacking' => NULL,
    // Bar chart only. Set to TRUE to stack.
    '#pre_render' => array(
      'charts_pre_render_element',
    ),
    '#tooltips' => TRUE,
    '#tooltips_use_html' => FALSE,
    '#data_labels' => FALSE,
    '#legend' => TRUE,
    '#legend_title' => NULL,
    '#legend_title_font_weight' => 'bold',
    // Options: normal, bold
    '#legend_title_font_style' => 'normal',
    // Options: normal, italic
    '#legend_title_font_size' => NULL,
    // CSS value for font size, e.g. 1em or 12px.
    '#legend_position' => 'right',
    // Options: top, right, bottom, left.
    '#legend_font_weight' => 'normal',
    // Options: normal, bold
    '#legend_font_style' => 'normal',
    // Options: normal, italic
    '#legend_font_size' => NULL,
    // CSS value for font size, e.g. 1em or 12px.
    '#width' => NULL,
    '#height' => NULL,
    '#attributes' => array(),
    // Applied to the wrapper, not the chart.
    '#chart_library' => NULL,
    // If requiring a particular charting module.
    '#raw_options' => array(),
  );

  // Properties for x and y axes.
  $axis_properties = array(
    '#axis_type' => 'linear',
    // Options: linear, logarithmic, datetime, labels.
    '#title' => '',
    '#title_color' => '#000',
    '#title_font_weight' => 'normal',
    // Options: normal, bold
    '#title_font_style' => 'normal',
    // Options: normal, italic
    '#title_font_size' => 12,
    // CSS value for font size, e.g. 1em or 12px.
    '#labels' => NULL,
    '#labels_color' => '#000',
    '#labels_font_weight' => 'normal',
    // Options: normal, bold
    '#labels_font_style' => 'normal',
    // Options: normal, italic
    '#labels_font_size' => NULL,
    // CSS value for font size, e.g. 1em or 12px.
    '#labels_rotation' => NULL,
    // Integer rotation value, e.g. 30, -60 or 90.
    '#grid_line_color' => '#ccc',
    '#base_line_color' => '#ccc',
    '#minor_grid_line_color' => '#e0e0e0',
    '#max' => NULL,
    // Integer max value on this axis.
    '#min' => NULL,
    // Integer minimum value on this axis.
    '#opposite' => FALSE,
  );
  $info['chart_xaxis'] = array() + $axis_properties;
  $info['chart_yaxis'] = array() + $axis_properties;

  // And then the pieces of charts used within chart types.
  $info['chart_data'] = array(
    '#title' => NULL,
    '#labels' => NULL,
    '#data' => array(),
    '#color' => NULL,
    '#show_in_legend' => TRUE,
    '#show_labels' => FALSE,
    // Show inline labels next to the data.
    '#chart_type' => NULL,
    // If building multicharts. The chart type, e.g. pie.
    '#line_width' => 1,
    // Line chart only.
    '#marker_radius' => 3,
    // Line chart only. Size in pixels, e.g. 1, 5.
    '#target_axis' => NULL,
    // If using multiple axes, key for the matching y axis.
    // Formatting options.
    '#decimal_count' => NULL,
    // The number of digits after the decimal separator. e.g. 2
    '#date_format' => NULL,
    // A custom date format, e.g. %Y-%m-%d
    '#prefix' => NULL,
    '#suffix' => NULL,
  );
  $info['chart_data_item'] = array(
    '#data' => NULL,
    '#color' => NULL,
    '#title' => NULL,
  );
  return $info;
}

/**
 * Implements hook_library().
 */
function charts_library() {
  $library['charts.admin'] = array(
    'title' => t('Charts admin integration'),
    'version' => '1.0',
    'js' => array(
      array(
        'data' => drupal_get_path('module', 'charts') . '/js/charts.admin.js',
        'type' => 'file',
        'weight' => 10,
      ),
    ),
  );
  return $library;
}

/**
 * Implements hook_permission().
 */
function charts_permission() {
  return array(
    'administer charts' => array(
      'title' => t('Administer Charts'),
      'description' => t('Visit the <a href="!url">Default chart configuration</a> (used for Charts administration)', array(
        '!url' => url('admin/config/content/charts'),
      )),
    ),
    'access example charts' => array(
      'title' => t('Access example charts'),
      'description' => t('Visit the <a href="!url">Charts examples</a> (used for Charts testing and demo)', array(
        '!url' => url('charts/examples'),
      )),
    ),
  );
}

/**
 * Implements hook_theme().
 */
function charts_theme() {
  return array(
    'charts_settings_fields' => array(
      'render element' => 'element',
    ),
    'charts_chart' => array(
      'render element' => 'element',
    ),
  );
}

/**
 * Implements hook_views_api().
 */
function charts_views_api() {
  return array(
    'api' => 3.0,
    'path' => drupal_get_path('module', 'charts') . '/views',
  );
}

/**
 * Main #pre_render callback to expand a chart element.
 */
function charts_pre_render_element($element) {
  $charts_info = charts_info();
  $chart_library = isset($element['#chart_library']) ? $element['#chart_library'] : NULL;

  // Use the first charting library if the requested library is not available.
  if (isset($chart_library) && isset($charts_info[$chart_library])) {
    $chart_library_info = $charts_info[$chart_library];
  }
  else {
    $chart_library = key($charts_info);
    $chart_library_info = $charts_info[$chart_library];
  }
  if (!isset($chart_library_info)) {
    $element['#type'] = 'markup';
    $element['#markup'] = t('No charting library found. Enable a charting module such as Google Charts or Highcharts.');
    return $element;
  }

  // Ensure there's an x and y axis to provide defaults.
  $chart_type = chart_get_type($element['#chart_type']);
  if ($chart_type['axis'] === CHARTS_DUAL_AXIS) {
    foreach (element_children($element) as $key) {
      $children_types[] = $element[$key]['#type'];
    }
    if (!in_array('chart_xaxis', $children_types)) {
      $element['xaxis'] = array(
        '#type' => 'chart_xaxis',
      );
    }
    if (!in_array('chart_yaxis', $children_types)) {
      $element['yaxis'] = array(
        '#type' => 'chart_yaxis',
      );
    }
  }

  // Convert integer properties to save library modules the hassle.
  charts_cast_element_integer_values($element);

  // Generic theme function assuming it will be suitable for most chart types.
  $element['#theme'] = 'charts_chart';

  // Allow the chart to be altered.
  $alter_hooks = array(
    'chart',
  );
  if ($element['#chart_id']) {
    $alter_hooks[] = 'chart_' . $element['#chart_id'];
  }
  drupal_alter($alter_hooks, $element, $element['#chart_id']);

  // Include the library specific file and render via their callback.
  if (isset($chart_library_info['file'])) {
    $module_path = drupal_get_path('module', $chart_library_info['module']);
    include_once $module_path . '/' . $chart_library_info['file'];
  }
  $callback = $chart_library_info['render'];
  $element = $callback($element);
  if (!empty($element['#chart_definition'])) {
    $chart_definition = $element['#chart_definition'];
    unset($element['#chart_definition']);

    // Allow the chart definition to be altered.
    $alter_hooks = array(
      'chart_definition',
    );
    if ($element['#chart_id']) {
      $alter_hooks[] = 'chart_definition_' . $element['#chart_id'];
    }
    drupal_alter($alter_hooks, $chart_definition, $element, $element['#chart_id']);

    // Set the element #chart_json property as a data-attribute.
    $element['#attributes']['data-chart'] = drupal_json_encode($chart_definition);
  }
  return $element;
}

/**
 * Retrieve a list of all charting libraries available.
 *
 * @see hook_charts_info()
 */
function charts_info() {
  $charts_info = array();
  foreach (module_implements('charts_info') as $module) {
    $module_charts_info = module_invoke($module, 'charts_info');
    foreach ($module_charts_info as $chart_library => $chart_library_info) {
      $module_charts_info[$chart_library]['module'] = $module;
    }
    $charts_info = array_merge($charts_info, $module_charts_info);
  }
  drupal_alter('charts_info', $charts_info);
  return $charts_info;
}

/**
 * Retrieve a specific chart library.
 */
function chart_get_library($library) {
  $info = charts_info();
  return $info[$library] ? $info[$library] : FALSE;
}

/**
 * Retrieve a list of all chart types available.
 *
 * @see hook_charts_type_info()
 */
function charts_type_info() {
  $charts_type_info = module_invoke_all('charts_type_info');

  // Populate defaults for all chart types.
  foreach ($charts_type_info as $chart_type => $chart_type_info) {
    $charts_type_info[$chart_type] += array(
      'label' => '',
      'axis' => CHARTS_DUAL_AXIS,
      'axis_inverted' => FALSE,
      'stacking' => FALSE,
    );
  }
  drupal_alter('charts_type_info', $charts_type_info);
  return $charts_type_info;
}

/**
 * Retrieve a specific chart type.
 */
function chart_get_type($chart_type) {
  $types = charts_type_info();
  return $types[$chart_type] ? $types[$chart_type] : FALSE;
}

/**
 * Implements hook_charts_type_info().
 */
function charts_charts_type_info() {
  $chart_types['pie'] = array(
    'label' => t('Pie'),
    'axis' => CHARTS_SINGLE_AXIS,
  );
  $chart_types['bar'] = array(
    'label' => t('Bar'),
    'axis' => CHARTS_DUAL_AXIS,
    'axis_inverted' => TRUE,
    // Meaning x/y axis are flipped.
    'stacking' => TRUE,
  );
  $chart_types['column'] = array(
    'label' => t('Column'),
    'axis' => CHARTS_DUAL_AXIS,
    'stacking' => TRUE,
  );
  $chart_types['line'] = array(
    'label' => t('Line'),
    'axis' => CHARTS_DUAL_AXIS,
  );
  $chart_types['area'] = array(
    'label' => t('Area'),
    'axis' => CHARTS_DUAL_AXIS,
    'stacking' => TRUE,
  );
  $chart_types['scatter'] = array(
    'label' => t('Scatter'),
    'axis' => CHARTS_DUAL_AXIS,
  );
  return $chart_types;
}

/**
 * Default colors used in all libraries.
 */
function charts_default_colors() {
  $form = variable_get('charts_default_settings');
  if (isset($form['colors'])) {
    return $form['colors'];
  }
  else {
    return array(
      '#2f7ed8',
      '#0d233a',
      '#8bbc21',
      '#910000',
      '#1aadce',
      '#492970',
      '#f28f43',
      '#77a1e5',
      '#c42525',
      '#a6c96a',
    );
  }
}

/**
 * Recursive function to trim out empty options that aren't used.
 */
function charts_trim_array(&$array) {
  foreach ($array as $key => &$value) {
    if (is_array($value)) {
      charts_trim_array($value);
    }
    elseif (is_null($value) || is_array($value) && count($value) === 0) {
      unset($array[$key]);
    }
  }
}

/**
 * Recursive function to cast integer values.
 */
function charts_cast_element_integer_values(&$element) {

  // Cast options to integers to avoid redundant library fixing problems.
  $integer_options = array(
    // Chart options.
    '#title_font_size',
    '#font_size',
    '#legend_title_font_size',
    '#legend_font_size',
    '#width',
    '#height',
    // Axis options.
    '#title_font_size',
    '#labels_font_size',
    '#labels_rotation',
    '#max',
    '#min',
    // Data options.
    '#decimal_count',
  );
  foreach ($element as $property_name => $value) {
    if (is_array($element[$property_name])) {
      charts_cast_element_integer_values($element[$property_name]);
    }
    elseif ($property_name && in_array($property_name, $integer_options)) {
      $element[$property_name] = is_null($element[$property_name]) || strlen($element[$property_name]) === 0 ? NULL : (int) $element[$property_name];
    }
  }
}

/**
 * Output the chart renderable as a string.
 */
function theme_charts_chart($variables) {
  $element = $variables['element'];
  $attributes = $element['#attributes'];
  $attributes['id'] = $element['#id'];
  $attributes['class'][] = 'chart';
  return '<div ' . drupal_attributes($attributes) . '>' . (isset($element['#chart']) ? $element['#chart'] : '') . '</div>';
}

Functions

Namesort descending Description
charts_cast_element_integer_values Recursive function to cast integer values.
charts_charts_type_info Implements hook_charts_type_info().
charts_default_colors Default colors used in all libraries.
charts_element_info Implements hook_element_info().
charts_info Retrieve a list of all charting libraries available.
charts_library Implements hook_library().
charts_menu Implements hook_menu().
charts_permission Implements hook_permission().
charts_pre_render_element Main #pre_render callback to expand a chart element.
charts_theme Implements hook_theme().
charts_trim_array Recursive function to trim out empty options that aren't used.
charts_type_info Retrieve a list of all chart types available.
charts_views_api Implements hook_views_api().
chart_get_library Retrieve a specific chart library.
chart_get_type Retrieve a specific chart type.
theme_charts_chart Output the chart renderable as a string.

Constants

Namesort descending Description
CHARTS_DUAL_AXIS Constant used in hook_charts_type_info() to declare chart types with a dual axes. Most charts use this type of data, meaning multiple categories each have multiple values. This type of data is usually represented as a table.
CHARTS_SINGLE_AXIS Constant used in hook_charts_type_info() to declare chart types with a single axis. For example a pie chart only has a single dimension.