d3.module in d3.js 7
D3 module file for creating visualizations with d3.js.
Sponsored by http://www.freeflowdigital.com
File
d3.moduleView source
<?php
/**
* @file
* D3 module file for creating visualizations with d3.js.
*
* Sponsored by http://www.freeflowdigital.com
*/
/**
* Implements hook_menu().
*/
function d3_menu() {
$items['admin/config/d3'] = array(
'title' => 'D3',
'description' => 'Configuration for d3.',
'position' => 'right',
'weight' => 20,
'page callback' => 'system_admin_menu_block_page',
'access arguments' => array(
'access administration pages',
),
'file path' => drupal_get_path('module', 'system'),
'file' => 'system.admin.inc',
);
$items['admin/config/d3/settings'] = array(
'title' => 'D3 Settings',
'description' => 'Configuration settings for D3',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'd3_config_form',
),
'access arguments' => array(
'administer d3',
),
'file' => 'd3.admin.inc',
);
return $items;
}
/**
* Implements hook_permission().
*/
function d3_permission() {
return array(
'administer d3' => array(
'title' => t('Administer d3'),
),
);
}
/**
* Implements hook_theme().
*/
function d3_theme() {
foreach (libraries_info() as $path => $library) {
// Call this function to get all fields - i.e. library path.
$library = libraries_detect($path);
// If a template was specified in the .info file or hook_libraries_info.
if (isset($library['template'])) {
// Change d3.[library name] to d3_[library name] for a theme key.
$theme_key = str_replace('.', '_', $library['machine name']);
$themes[$theme_key] = array(
'variables' => array(),
'template' => $library['template'],
'path' => $library['library path'],
);
}
}
$themes['d3'] = array(
'variables' => array(
'type' => NULL,
'library' => array(),
'vis_id' => NULL,
),
'template' => 'd3',
);
$themes['d3_graphapi'] = array(
'variables' => array(
'graph' => NULL,
'config' => NULL,
),
);
return $themes;
}
/**
* Lists library versions by polling CDN
*/
function _d3_list_library_versions_from_cdn() {
$library_versions = array();
if (checkdnsrr('d3js.org')) {
stream_context_set_default(array(
'https' => array(
'method' => 'HEAD',
),
));
for ($library_version = 3; $library_version < 50; ++$library_version) {
$hdrs = get_headers("https://d3js.org/d3.v{$library_version}.min.js");
if ($hdrs) {
if (substr($hdrs[0], 9, 1) === '2') {
$library_versions[] = "{$library_version}";
continue;
}
}
if (count($library_versions) != 0) {
break;
}
}
stream_context_set_default(array(
'https' => array(
'method' => 'GET',
),
));
}
return $library_versions;
}
/**
* Lists library versions nominally available in sites/all/libraries/d3/
*/
function _d3_list_library_versions_from_libraries() {
$library_versions = array();
$path = libraries_get_path('d3');
if ($path) {
$files = array();
// In the repository the files might be named d3.js and d3.min.js.
$files += file_scan_directory($path, '/d3.js|d3.min.js/');
// They could also have the version # in the file name.
$files += file_scan_directory($path, '/d3.v[0-9]+(.min)?.js/');
foreach ($files as $file) {
$library_version = '';
// Assume files named d3.js or d3.min.js are version 3
if ($file->filename == 'd3.js' || $file->filename == 'd3.min.js') {
$library_version = 3;
}
elseif (preg_match('/d3.v([0-9]+)(.min)?.js/', $file->filename, $matches)) {
$library_version = $matches[1];
}
if ($library_version && !in_array($library_version, $library_versions)) {
$library_versions[] = "{$library_version}";
}
}
}
sort($library_versions, SORT_NUMERIC);
return $library_versions;
}
/**
* Implements hook_preprocess_d3() for d3.tpl.php.
*/
function d3_preprocess_d3(&$variables) {
// Let drupal_attributes render this.
$variables['classes_array'][] = $variables['type'];
$variables['attributes_array']['id'] = array(
$variables['vis_id'],
);
$library = isset($variables['library']) ? $variables['library'] : FALSE;
// Let's look for a template file in the library definition.
if (isset($library['template'])) {
$variables['theme_hook_suggestion'] = isset($library['library name']) ? $library['library name'] : str_replace('.', '_', $library['machine name']);
}
}
/**
* Implements hook_libraries_info_file_paths().
*/
function d3_libraries_info_file_paths() {
// Get all library directories.
$libraries = libraries_get_libraries();
$paths = array();
// Output an array of paths to check for.
foreach ($libraries as $path) {
$paths[] = $path;
}
// Load the directory where the d3 example libraries are.
$example_path = drupal_get_path('module', 'd3') . '/libraries/';
// Add these to the search directories for libraries.
foreach (d3_default_libraries() as $example) {
$paths[] = $example_path . $example;
}
return $paths;
}
/**
* Library post-detect callback to process info files before caching.
*
* @see libraries_detect()
* @see libraries_load()
*/
function d3_detect_library_info(&$library, $version = NULL, $variant = NULL) {
$handlers = d3_get_library_info_handlers_info();
foreach ($handlers as $type => $info) {
$handler = d3_get_library_info_handler($type);
$handler
->setLibrary($library);
$handler->processor
->process();
}
// Cycle through d3 libraries and process their info files.
}
/**
* Get a handler object for a library.
*
* @param string $type
* The handler type. i.e. 'views'.
*
* @return $object
* Instance of D3LibraryInfoController
*
* @see hook_library_info_handlers()
*/
function d3_get_library_info_handler($key = 'default') {
static $handlers;
if (!$handlers) {
$handlers = d3_get_library_info_handlers_info();
}
$handler = !empty($handlers[$key]) ? $handlers[$key] : $handlers['default'];
if (class_exists($handler['controller'])) {
// Instantiate controller class.
$controller = new $handler['controller']($handler);
if ($controller instanceof D3LibraryInfoController) {
// These are the info file keys that will be processed.
$controller->processor
->setKeys(array(
$key,
));
}
}
return $controller;
}
/**
* Get information on all info handlers.
*
* @return
* Array of library info handler info arrays.
*/
function d3_get_library_info_handlers_info() {
$handlers = array(
'default' => array(
'processor' => 'D3LibraryInfoProcessor',
'controller' => 'D3LibraryInfoController',
'mapping' => 'D3DataMapping',
),
);
// Allow submodules of d3 to define new library info handlers.
$handlers += module_invoke_all('library_info_handlers');
// Allow submodules of d3 to alter current info handlers.
drupal_alter('library_info_handlers', $handlers);
return $handlers;
}
/**
* Implements hook_libraries_info_alter().
*/
function d3_libraries_info_alter(&$libraries) {
foreach (d3_get_libraries() as $library_name => $library) {
// Automatically add in the d3.drupal dependency so that each
// d3.library doesn't have to.
$libraries[$library_name]['dependencies'][] = 'd3.drupal';
// Add in a function to process additional info file information.
$libraries[$library_name]['callbacks']['post-detect'][] = 'd3_detect_library_info';
}
// By default, the libraries module only checks the libraries folders.
// We need to add this module's libraries path to the library info.
$path = drupal_get_path('module', 'd3') . '/libraries/';
foreach (d3_default_libraries() as $library_name) {
// Change library path to path/to/module/d3/libraries
$libraries[$library_name]['library path'] = $path . $library_name;
}
}
/**
* Implements hook_libraries_info().
*/
function d3_libraries_info() {
$libraries = array();
// Drupal ext adds behaviors and d3 global object.
$libraries['d3.drupal'] = array(
'name' => 'D3 Drupal ext',
'vendor url' => 'http://drupal.org/sandbox/asherry/1477334',
'files' => array(
'js' => array(
'd3.js',
),
),
'path' => 'js',
'library path' => drupal_get_path('module', 'd3'),
'dependencies' => array(
'd3',
),
'version' => '1',
);
$lib = variable_get('d3_library_source', 'lib');
$ver = variable_get('d3_library_version', 3);
if ($lib == 'cdn') {
$options = array(
'lines' => 4,
'file' => "https://d3js.org/d3.v{$ver}.min.js",
);
$libraries['d3'] = array(
'name' => 'D3',
'vendor url' => 'https://d3js.org/',
'download url' => 'https://d3js.org/',
// This will bypass the check for file_exists.
'installed' => TRUE,
// For easy reference later.
'cdn' => TRUE,
'library path' => 'https://d3js.org',
'files' => array(
'js' => array(
'data' => "d3.v{$ver}.min.js",
),
),
'version' => $ver,
);
return $libraries;
}
// Path to library, (if installed).
$path = libraries_get_path('d3');
if ($path) {
$files = array();
// In the repository the files might me named d3.js and d3.min.js.
$files += file_scan_directory($path, '/d3.js|d3.min.js/');
// They could also have the version # in the file name.
$files += file_scan_directory($path, '/d3.v[0-9](.min)?.js/');
// If we don't have any files we shouldn't add the library at all.
// This will fire drupal error and direct the user to reports.
if (count($files) == 0) {
return $libraries;
}
$file = NULL;
// Find the file that matches the specified version.
foreach ($files as $a_file) {
// Assume files named d3.js or d3.min.js are version 3
if ($ver === 3 && ($a_file->filename === "d3.js" || $a_file->filename === "d3.min.js") || $a_file->filename === "d3.v{$ver}.js" || $a_file->filename === "d3.v{$ver}.min.js") {
$file = $a_file;
}
}
if (!$file) {
$file = array_shift($files);
}
$libraries['d3'] = array(
'name' => 'D3',
'vendor url' => 'https://d3js.org/',
'download url' => 'https://d3js.org/',
'files' => array(
'js' => array(
$file->filename,
),
),
'version' => $ver,
);
}
return $libraries;
}
/**
* Helper callback to return all sample libraries located inside this module.
*/
function d3_default_libraries() {
return array(
'd3.columnchart',
'd3.extend',
'd3.forcedirected',
'd3.module_dependencies',
'd3.linegraph',
'd3.tooltip',
'd3.barchart',
'd3.piechart',
);
}
/**
* D3's main API callback.
*
* @param array $vis
* The chart data
*
* @return string
* Rendered chart html
*/
function d3_draw($vis) {
// Type is required for correct processing.
$type = !empty($vis['type']) ? strtolower($vis['type']) : FALSE;
if (!$type) {
drupal_set_message(t('No chart type specified'), 'error');
return;
}
// Ensure we have d3 installed, don't just show a blank screen.
$d3 = libraries_load('d3');
if (empty($d3['installed'])) {
drupal_set_message(t('The d3 library is not correctly installed'), 'error');
return;
}
$output = array(
'#theme' => 'd3',
);
// If user selected cdn, we need to load this manually.
// @see http://drupal.org/node/864376
if (!empty($d3['cdn'])) {
$output['#attached']['js'][] = array(
'data' => $d3['library path'] . '/' . $d3['files']['js']['data'],
'type' => 'external',
'group' => JS_LIBRARY,
);
}
// Form library name - convention is going to be d3.[library].
$library_name = 'd3.' . $type;
// Check for library instance / definition.
if ($lib = libraries_load($library_name)) {
// Enforce lowercase change.
$output['#library'] = $lib + array(
'library name' => str_replace('.', '_', $library_name),
);
$output['#type'] = $type;
// Store as vis_id because key ['id'] will get overridden.
$output['#vis_id'] = $vis['id'];
// Now add to the behaviors to execute on page load.
$js = array(
'd3' => array(
'inventory' => array(
$vis['id'] => $vis,
),
),
);
$output['#attached']['js'][] = array(
'data' => $js,
'type' => 'setting',
);
// Renders the html - .tpl.php if specified or default d3.tpl.php.
return render($output);
}
else {
drupal_set_message(t('Invalid chart type %type and/or library %library_name is not installed', array(
'%type' => $type,
'%library_name' => $library_name,
)));
return '';
}
}
/**
* Provides an array of d3 libraries.
*
* D3 libraries are going to have a prefix of d3., see README.txt
* for information on creating a custom d3 library.
*/
function d3_get_libraries() {
static $d3_libraries;
if ($d3_libraries) {
return $d3_libraries;
}
// Returns all libraries in the default folders.
$libraries = libraries_info();
foreach ($libraries as $library) {
$library_name = $library['machine name'];
// Filter out any other non-d3 library. All d3 libraries should have
// the prefix "d3.".
if (strpos($library_name, 'd3.') === FALSE) {
continue;
}
// Do not list these default extension libraries.
if (in_array($library_name, array(
'd3.extend',
'd3.tooltip',
'd3.drupal',
))) {
continue;
}
$d3_libraries[$library_name] = $library;
$d3_libraries[$library_name]['js callback'] = str_replace('d3.', '', $library_name);
}
return $d3_libraries;
}
/**
* Implements hook_graphapi_formats().
*/
function d3_graphapi_formats() {
return array(
'd3' => t('D3'),
);
}
/**
* Implements hook_graphapi_settings_form().
*/
function d3_graphapi_settings_form($values) {
$engine = 'd3';
$form[$engine] = array(
'#type' => 'fieldset',
'#title' => 'D3 settings',
);
$options = array();
foreach (d3_get_libraries() as $library) {
$options[$library['js callback']] = $library['name'];
}
$form[$engine]['library'] = array(
'#title' => 'Default library',
'#description' => t('Use this d3 library as the default to render graphapi visualizations'),
'#type' => 'select',
'#options' => $options,
'#default_value' => isset($values['library']) ? $values['library'] : key($options),
);
return $form;
}
/**
* Converts $graph data array two arrays for links and nodes.
*
* @param array $graph
* Associative array of nodes with link information and data.
*
* @return array
* Links and nodes.
*/
function _d3_graphapi_format_graph_data($graph) {
$data = array();
$indices = array();
$index = 0;
foreach ($graph as $id => $node) {
$indices[$id] = $index;
$index++;
}
// Add in edges.
foreach ($graph as $id => $node) {
$index = $indices[$id];
$data['nodes'][$index] = array(
'name' => $node['data']['title'],
'group' => isset($node['data']['group']) ? $node['data']['group'] : 1,
'data' => $node['data'],
);
if (count($node['edges']) > 0) {
foreach ($node['edges'] as $edge => $edge_data) {
$value = isset($edge_data['data']['value']) ? (int) $edge_data['data']['value'] : NULL;
$data['links'][] = array(
'data' => isset($edge_data['data']) ? $edge_data['data'] : array(),
'source' => $index,
'target' => $indices[$edge],
// TODO: This is hard coded, it could be dynamic.
'value' => $value,
);
}
}
}
return $data;
}
/**
* Displays the visualization for graphapi's selected library.
*/
function theme_d3_graphapi($vars) {
if (empty($vars['graph'])) {
return '';
}
$default_library = variable_get('d3_graphapi_default_library', 'forcedirected');
$library = empty($vars['config']['library']) ? $default_library : $vars['config']['library'];
$graph = _d3_graphapi_format_graph_data($vars['graph']);
$chart = array(
'id' => $vars['config']['id'],
'type' => $library,
'links' => $graph['links'],
'nodes' => $graph['nodes'],
'config' => $vars['config'],
);
return d3_draw($chart);
}
/**
* Implements hook_graphapi_default_settings().
*
* We reuse the default settings from thejit_forcedirected_default_settings.
*
* @see thejit_forcedirected_default_settings()
* @see views_object::option_definition()
*/
function d3_graphapi_default_settings() {
return array(
'd3' => array(
'library' => array(
'default' => 'd3.forcedirected',
),
),
);
}
Functions
Name | Description |
---|---|
d3_default_libraries | Helper callback to return all sample libraries located inside this module. |
d3_detect_library_info | Library post-detect callback to process info files before caching. |
d3_draw | D3's main API callback. |
d3_get_libraries | Provides an array of d3 libraries. |
d3_get_library_info_handler | Get a handler object for a library. |
d3_get_library_info_handlers_info | Get information on all info handlers. |
d3_graphapi_default_settings | Implements hook_graphapi_default_settings(). |
d3_graphapi_formats | Implements hook_graphapi_formats(). |
d3_graphapi_settings_form | Implements hook_graphapi_settings_form(). |
d3_libraries_info | Implements hook_libraries_info(). |
d3_libraries_info_alter | Implements hook_libraries_info_alter(). |
d3_libraries_info_file_paths | Implements hook_libraries_info_file_paths(). |
d3_menu | Implements hook_menu(). |
d3_permission | Implements hook_permission(). |
d3_preprocess_d3 | Implements hook_preprocess_d3() for d3.tpl.php. |
d3_theme | Implements hook_theme(). |
theme_d3_graphapi | Displays the visualization for graphapi's selected library. |
_d3_graphapi_format_graph_data | Converts $graph data array two arrays for links and nodes. |
_d3_list_library_versions_from_cdn | Lists library versions by polling CDN |
_d3_list_library_versions_from_libraries | Lists library versions nominally available in sites/all/libraries/d3/ |