You are here

class d3_views_plugin_style_d3 in d3.js 7

Style plugin to render a d3 visualization

Hierarchy

Expanded class hierarchy of d3_views_plugin_style_d3

1 string reference to 'd3_views_plugin_style_d3'
d3_views_plugins in modules/d3_views/views/d3_views.views.inc
Implements hook_views_plugins().

File

modules/d3_views/views/plugins/d3_views_plugin_style_d3.inc, line 20
Contains the d3 style plugin.

View source
class d3_views_plugin_style_d3 extends views_plugin_style {

  /**
   * Statically store all library info.
   *
   * @var array
   */
  protected $libraries = array();

  /**
   * Statically store the current visualization.
   *
   * @var array
   */
  protected $vis = array();

  /**
   * Local instance of D3ViewsLibraryInfoController
   *
   * @var D3ViewsLibraryInfoController
   */
  protected $controller;

  /**
   * Override views_object::construct().
   */
  function construct() {
    parent::construct();
  }
  function init(&$view, &$display, $options = NULL) {
    parent::init($view, $display, $options);
    $this->controller = d3_get_library_info_handler('views');
    $this->controller->mapping
      ->setPlugin($this);
  }

  /**
   * Set default options.
   */
  function option_definition() {
    $options = parent::option_definition();
    $options['library'] = array(
      'default' => FALSE,
    );
    $options['fields'] = array(
      'default' => array(),
    );
    $options['settings'] = array(
      'default' => array(),
    );
    $options['show_table'] = array(
      'default' => FALSE,
    );
    return $options;
  }

  /**
   * Options form for the given style.
   */
  function options_form(&$form, &$form_state) {
    parent::options_form($form, $form_state);

    // We temporarily disable grouping on all libraries.
    // @TODO In future releases there should be an option to group
    // if the visualization's data requires it.
    $form['grouping']['#access'] = FALSE;
    $handlers = $this->display->handler
      ->get_handlers('field');

    // Reload the library from $form_state if called by ajax.
    $library = $this
      ->getLibrary($form_state);

    // Reset the library controller.
    $this->controller
      ->setLibrary($library);
    $form['library'] = array(
      '#title' => 'Library',
      '#description' => t('Select which d3 library you would like to use with this view. Note: For instructions on how to incorporate your custom library with views, see the README.txt.'),
      '#type' => 'select',
      '#options' => $this
        ->getLibraryOptions(),
      '#default_value' => $this->controller
        ->machineName(),
      '#ajax' => array(
        'path' => views_ui_build_form_url($form_state),
      ),
    );
    $this->controller->mapping
      ->form($form, $form_state);

    // Additional settings.
    $form['settings'] = array(
      '#type' => 'container',
      '#tree' => TRUE,
    );

    // Can eventually separate these out into different functions.
    foreach ($this->controller
      ->getSettings() as $key => $data) {
      $form_element = !empty($data['_info']['form_element']) ? $data['_info']['form_element'] : array();
      $form['settings'][$key] = $form_element + array(
        '#type' => 'textfield',
        '#default_value' => NULL,
        '#title' => '',
        '#description' => '',
      );
      if (!empty($this->options['settings'][$key])) {
        $form['settings'][$key]['#default_value'] = $this->options['settings'][$key];
      }
    }
    $form['show_table'] = array(
      '#title' => 'Show table',
      '#type' => 'checkbox',
      '#default_value' => $this->options['show_table'],
    );
    if (empty($handlers)) {
      $form['error_markup'] = array(
        '#markup' => '<div class="error messages">' . t('You need at least one field before you can configure your table settings') . '</div>',
      );
      return;
    }

    // Note: views UI registers this theme handler on our behalf. Your module
    // will have to register your theme handlers if you do stuff like this.
    $form['#theme'] = 'views_ui_style_d3_options_form';
  }

  /**
   * Get the current field mapping's aggregation setting.
   *
   * @param string $key1
   *   The base view field key.
   * @param string $key2
   *   For view fields that have sub-fields, this is the sub-field key.
   *
   * @return string
   *   Setting for the particular aggregation, or FALSE.
   */
  function getAggregation($key1, $key2 = FALSE) {
    if ($key2) {
      return !empty($this->options['fields'][$key1][$key2]['aggregation']) ? $this->options['fields'][$key1][$key2]['aggregation'] : FALSE;
    }
    return !empty($this->options['fields'][$key1]['aggregation']) ? $this->options['fields'][$key1]['aggregation'] : FALSE;
  }

  /**
   * Default value for a field mapping.
   *
   * @param string $key1
   *   The base view field key.
   * @param string $key2
   *   For view fields that have sub-fields, this is the sub-field key.
   *
   * @return object
   *   Object with field, and aggregation properties.
   */
  function getDefaultValues($key1, $key2 = NULL, $form_state = NULL) {
    $default = new StdClass();
    $default->field = FALSE;
    $default->aggregation = FALSE;
    $options = $this
      ->getFieldOptions($form_state);
    if (!is_null($key2)) {
      return !empty($options[$key1][$key2]) ? (object) $options[$key1][$key2] : $default;
    }
    return !empty($options[$key1]) ? (object) $options[$key1] : $default;
  }

  /**
   * Get data saved to views, or from form state for options.
   */
  function getFieldOptions($form_state = NULL) {
    $options = $form_state && !empty($form_state['input']['style_options']) ? $form_state['input']['style_options'] : $this->options;
    return $options['fields'];
  }

  /**
   * Wrapper function to determine the methods to use, and execute it.
   */
  function aggregate() {
    $vis =& $this
      ->getVis();
    $map = $this->controller->mapping
      ->getMapping();
    $fields = $this->controller
      ->getFields();

    // Loop through mapping to get aggregation settings.
    foreach ($map as $key => $values) {

      // Has subfields.
      if (is_array($values)) {

        // Convert the settings to numeric to match the already numeric array.
        $numeric = !empty($fields[$key]['_info']['data_type']) && $fields[$key]['_info']['data_type'] == '2dnnv';
        $index = 0;
        foreach ($values as $k => $v) {

          // We have to pas the field name to the getAggregation function, whereas
          // we need to pass the index to the aggregation function.
          // Only if it's a numeric array.
          if (($aggregation = $this
            ->getAggregation($key, $k)) && method_exists($this, $aggregation . 'Multiple')) {
            $k = $numeric ? $index : $k;
            $vis[$key] = $this
              ->{$aggregation . 'Multiple'}($vis[$key], $k);
          }
          $index++;
        }
      }
      else {
        if (($aggregation = $this
          ->getAggregation($key)) && method_exists($this, $aggregation)) {
          $vis[$key] = $this
            ->{$aggregation}($vis[$key]);
        }
      }
    }
  }

  /**
   * Aggregation function for simple arrays.
   */
  function unique($data) {
    return array_values(array_unique($data));
  }

  /**
   * Aggregation function for multidimensional arrays.
   */
  function uniqueMultiple($data, $field_name) {

    // TODO
  }

  /**
   * Aggregation function for simple arrays.
   */
  function count($data) {
    return array_count_values($data);
  }

  /**
   * Aggregation function for multidimensional arrays.
   */
  function countMultiple($data, $field_name) {
    $counted = array();

    // Restructure to group by given $field_name.
    foreach ($data as $datum) {
      $counted[$datum[$field_name]][] = $datum;
    }

    // Loop through, count each subarray.
    foreach ($counted as $field_id => &$items) {
      $count = count($items);

      // Grab the first item of each subarray with original values.
      $items = reset($items);
      $items[$field_name] = $count;
    }
    return array_values($counted);
  }
  function map($rows) {
    $this->controller->mapping
      ->map($rows);
  }

  /**
   * Additional functionality on data before it is passed to d3_draw.
   *
   * TODO: there is some chance this code could actually be put in
   *       the D3LibraryInfoProcessor class itselfa s this functionality
   *       wolud be the same for all libraries.
   */
  function process() {
    $vis =& $this
      ->getVis();

    // Add settings to visualization.
    if (!empty($this->options['settings'])) {
      foreach ($this->controller
        ->getSettings() as $key => $setting) {
        if (isset($this->options['settings'][$key])) {
          $vis[$key] = $this->options['settings'][$key];
        }
      }
    }
  }

  /**
   * Render the display in this style.
   *
   * @see template_preprocess_d3_views_view_d3().
   */
  function render() {
    if ($this
      ->uses_row_plugin() && empty($this->row_plugin)) {
      debug('views_plugin_style_default: Missing row plugin');
      return;
    }
    $library = $this
      ->getLibrary();
    $this->controller
      ->setLibrary($library);

    // This is views theming, send this over to views preprocess.
    return theme($this
      ->theme_functions(), array(
      'view' => $this->view,
      'options' => $this->options,
      'rows' => $this
        ->render_fields($this->view->result),
    ));
  }

  /**
   * Send the vis to d3's theme handlers.
   *
   * @see template_preprocess_d3_views_view_d3().
   */
  function draw() {
    return d3_draw($this
      ->getVis());
  }

  /**
   * Get the current visualization by reference.
   */
  public function &getVis() {
    if (!$this->vis) {
      $libraries = $this
        ->getLibraries();
      $this->vis = array(
        'id' => 'view-d3-vis-' . $this->view->dom_id,
        'type' => $libraries[$this->options['library']]['js callback'],
      );
    }
    return $this->vis;
  }

  /**
   * Get the fully loaded from the form, or current library.
   *
   * @param Array $form_state
   *   Form API $form_state array.
   */
  function getLibrary($form_state = NULL) {
    if (!empty($form_state['values']['style_options']['library'])) {
      $library = $form_state['values']['style_options']['library'];
    }
    else {
      $library = $this->options['library'];
    }
    return !empty($library) ? libraries_load($library) : FALSE;
  }

  /**
   * Local function to get all libraries that are compatible with views.
   *
   * @return array
   *   Array of library info keyed by library machine name.
   */
  function getLibraryOptions() {
    $options = array();
    foreach ($this
      ->getLibraries() as $library) {

      // We can match compatibility based on the views API version.
      $library_name = $library['machine name'];
      $options[$library_name] = $library['name'];
    }
    return $options;
  }

  /**
   * Get d3 libraries basic info.
   *
   * WARNING: This does not return field / settings info. For that info
   * a full libraries_load is needed.
   */
  function getLibraries() {
    if (!empty($this->libraries)) {
      return $this->libraries;
    }
    $views_api_version = views_api_version();

    // List all d3 libraries.
    $libraries = d3_get_libraries();
    $this->libraries = array();
    foreach ($libraries as $library) {

      // Skip if no views integration.
      if (empty($library['views'])) {
        continue;
      }

      // Assume library is compatible with any version if it is not specified.
      if (empty($library['views']['version'])) {
        $library['views']['version'] = $views_api_version;
      }
      if (empty($library['views']['fields'])) {
        $library['views']['fields'] = array();
      }

      // Only add to the list if the version is compatible.
      if ($library['views']['version'] <= $views_api_version) {
        $this->libraries[$library['machine name']] = $library;
      }
    }
    return $this->libraries;
  }

  /**
   * Check user field mapping matches library requirements.
   */
  function checkFieldType($handler, $field_type) {
    $message = '';
    switch ($field_type) {
      case 'integer':

        // If it's a numeric field handler, this passes.
        if ($handler->definition['handler'] == 'views_handler_field_numeric') {
          break;
        }

        // If it's a field API handler, and a number field type, this passes.
        if ($handler->definition['handler'] == 'views_handler_field_field' && $handler->field_info['type'] == 'number_integer') {
          break;
        }

        // All other cases should fail.
        $message = 'Error: numeric field required for integer type.';
        break;
    }
    return '<span class="error">' . $message . '</span>';
  }

}

Members

Namesort descending Modifiers Type Description Overrides
d3_views_plugin_style_d3::$controller protected property Local instance of D3ViewsLibraryInfoController
d3_views_plugin_style_d3::$libraries protected property Statically store all library info.
d3_views_plugin_style_d3::$vis protected property Statically store the current visualization.
d3_views_plugin_style_d3::aggregate function Wrapper function to determine the methods to use, and execute it.
d3_views_plugin_style_d3::checkFieldType function Check user field mapping matches library requirements.
d3_views_plugin_style_d3::construct function Override views_object::construct(). Overrides views_object::construct
d3_views_plugin_style_d3::count function Aggregation function for simple arrays.
d3_views_plugin_style_d3::countMultiple function Aggregation function for multidimensional arrays.
d3_views_plugin_style_d3::draw function Send the vis to d3's theme handlers.
d3_views_plugin_style_d3::getAggregation function Get the current field mapping's aggregation setting.
d3_views_plugin_style_d3::getDefaultValues function Default value for a field mapping.
d3_views_plugin_style_d3::getFieldOptions function Get data saved to views, or from form state for options.
d3_views_plugin_style_d3::getLibraries function Get d3 libraries basic info.
d3_views_plugin_style_d3::getLibrary function Get the fully loaded from the form, or current library.
d3_views_plugin_style_d3::getLibraryOptions function Local function to get all libraries that are compatible with views.
d3_views_plugin_style_d3::getVis public function Get the current visualization by reference.
d3_views_plugin_style_d3::init function Initialize a style plugin. Overrides views_plugin_style::init
d3_views_plugin_style_d3::map function
d3_views_plugin_style_d3::options_form function Options form for the given style. Overrides views_plugin_style::options_form
d3_views_plugin_style_d3::option_definition function Set default options. Overrides views_plugin_style::option_definition
d3_views_plugin_style_d3::process function Additional functionality on data before it is passed to d3_draw.
d3_views_plugin_style_d3::render function Render the display in this style. Overrides views_plugin_style::render
d3_views_plugin_style_d3::unique function Aggregation function for simple arrays.
d3_views_plugin_style_d3::uniqueMultiple function Aggregation function for multidimensional arrays.
views_object::$definition public property Handler's definition.
views_object::$options public property Except for displays, options for the object will be held here. 1
views_object::altered_option_definition function Collect this handler's option definition and alter them, ready for use.
views_object::export_option public function 1
views_object::export_options public function
views_object::export_option_always public function Always exports the option, regardless of the default value.
views_object::options Deprecated public function Set default options on this object. 1
views_object::set_default_options public function Set default options.
views_object::set_definition public function Let the handler know what its full definition is.
views_object::unpack_options public function Unpack options over our existing defaults, drilling down into arrays so that defaults don't get totally blown away.
views_object::unpack_translatable public function Unpack a single option definition.
views_object::unpack_translatables public function Unpacks each handler to store translatable texts.
views_object::_set_option_defaults public function
views_plugin::$display public property The current used views display.
views_plugin::$plugin_name public property The plugin name of this plugin, for example table or full.
views_plugin::$plugin_type public property The plugin type of this plugin, for example style or query.
views_plugin::$view public property The top object of a view. Overrides views_object::$view 1
views_plugin::additional_theme_functions public function Provide a list of additional theme functions for the theme info page.
views_plugin::options_submit public function Handle any special handling on the validate form. 9
views_plugin::plugin_title public function Return the human readable name of the display.
views_plugin::summary_title public function Returns the summary of the settings in the display. 8
views_plugin::theme_functions public function Provide a full list of possible theme templates used by this style.
views_plugin_style::$row_plugin public property The row plugin, if it's initialized and the style itself supports it.
views_plugin_style::$row_tokens public property Store all available tokens row rows.
views_plugin_style::build_sort public function Called by the view builder to see if this style handler wants to interfere with the sorts. If so it should build; if it returns any non-TRUE value, normal sorting will NOT be added to the query. 1
views_plugin_style::build_sort_post public function Called by the view builder to let the style build a second set of sorts that will come after any other sorts in the view. 1
views_plugin_style::destroy public function Destructor. Overrides views_object::destroy
views_plugin_style::even_empty public function Should the output of the style plugin be rendered even if it's empty. 1
views_plugin_style::get_field public function Get a rendered field.
views_plugin_style::get_field_value public function Get the raw field value.
views_plugin_style::get_row_class public function Return the token replaced row class for the specified row.
views_plugin_style::options_validate public function Validate the options form. Overrides views_plugin::options_validate
views_plugin_style::pre_render public function Allow the style to do stuff before each row is rendered.
views_plugin_style::query public function Add anything to the query that we might need to. Overrides views_plugin::query 2
views_plugin_style::render_fields public function Render all of the fields for a given style and store them on the object.
views_plugin_style::render_grouping public function Group records as needed for rendering.
views_plugin_style::render_grouping_sets public function Render the grouping sets.
views_plugin_style::tokenize_value public function Take a value and apply token replacement logic to it.
views_plugin_style::uses_fields public function Return TRUE if this style also uses fields.
views_plugin_style::uses_row_class public function Return TRUE if this style also uses a row plugin.
views_plugin_style::uses_row_plugin public function Return TRUE if this style also uses a row plugin.
views_plugin_style::uses_tokens public function Return TRUE if this style uses tokens.
views_plugin_style::validate public function Validate that the plugin is correct and can be saved. Overrides views_plugin::validate