countdown-8.x-1.8/src/Plugin/CountdownLibrary/Flip.php
src/Plugin/CountdownLibrary/Flip.php
<?php
declare(strict_types=1);
namespace Drupal\countdown\Plugin\CountdownLibrary;
use Drupal\Core\Form\FormStateInterface;
use Drupal\countdown\Plugin\CountdownLibraryPluginBase;
/**
* PQINA Flip countdown library plugin implementation.
*
* Flip is a modern, high-performance countdown timer with smooth
* animations and a clean, minimalist design. Part of the PQINA
* suite of JavaScript components.
*
* @CountdownLibrary(
* id = "flip",
* label = @Translation("PQINA Flip"),
* description = @Translation("Modern timer with smooth flip animations"),
* type = "external",
* homepage = "https://pqina.nl/flip",
* repository = "https://github.com/pqina/flip",
* version = "1.8.4",
* npm_package = "@pqina/flip",
* folder_names = {
* "flip",
* "@pqina-flip",
* "pqina-flip",
* "flip-master",
* "pqina-flip-master"
* },
* init_function = "Flip",
* author = "PQINA",
* license = "MIT",
* dependencies = {
* "core/drupal",
* "core/once"
* },
* weight = 2,
* experimental = false,
* api_version = "1.0"
* )
*/
final class Flip extends CountdownLibraryPluginBase {
/**
* Available easing functions for flip animations.
*
* @var array
*/
protected const EASING_FUNCTIONS = [
'ease-linear' => 'Linear',
'ease-in-sine' => 'Ease In Sine',
'ease-out-sine' => 'Ease Out Sine',
'ease-in-out-sine' => 'Ease In Out Sine',
'ease-in-cubic' => 'Ease In Cubic',
'ease-out-cubic' => 'Ease Out Cubic',
'ease-in-out-cubic' => 'Ease In Out Cubic',
'ease-in-quad' => 'Ease In Quad',
'ease-out-quad' => 'Ease Out Quad',
'ease-in-out-quad' => 'Ease In Out Quad',
'ease-in-quart' => 'Ease In Quart',
'ease-out-quart' => 'Ease Out Quart',
'ease-in-out-quart' => 'Ease In Out Quart',
'ease-in-expo' => 'Ease In Expo',
'ease-out-expo' => 'Ease Out Expo',
'ease-in-out-expo' => 'Ease In Out Expo',
'ease-in-circ' => 'Ease In Circ',
'ease-out-circ' => 'Ease Out Circ',
'ease-in-out-circ' => 'Ease In Out Circ',
'ease-in-back' => 'Ease In Back',
'ease-out-back' => 'Ease Out Back',
'ease-in-out-back' => 'Ease In Out Back',
'ease-out-elastic' => 'Ease Out Elastic',
'ease-out-bounce' => 'Ease Out Bounce',
];
/**
* {@inheritdoc}
*/
public function getAssetMap(): array {
return [
'local' => [
'js' => [
'development' => 'dist/flip.js',
'production' => 'dist/flip.min.js',
],
'css' => [
'development' => 'dist/flip.css',
'production' => 'dist/flip.min.css',
],
],
'cdn' => [
'jsdelivr' => [
'js' => '//cdn.jsdelivr.net/npm/@pqina/flip@1.8.4/dist/flip.min.js',
'css' => '//cdn.jsdelivr.net/npm/@pqina/flip@1.8.4/dist/flip.min.css',
],
'unpkg' => [
'js' => '//unpkg.com/@pqina/flip@1.8.4/dist/flip.min.js',
'css' => '//unpkg.com/@pqina/flip@1.8.4/dist/flip.min.css',
],
'cdnjs' => [
'js' => '//cdnjs.cloudflare.com/ajax/libs/flip/1.8.4/flip.min.js',
'css' => '//cdnjs.cloudflare.com/ajax/libs/flip/1.8.4/flip.min.css',
],
],
];
}
/**
* {@inheritdoc}
*/
public function getDependencies(): array {
return [
'core/drupal',
'core/once',
];
}
/**
* {@inheritdoc}
*/
public function getHomepage(): ?string {
return 'https://pqina.nl/flip';
}
/**
* {@inheritdoc}
*/
protected function detectVersionCustom(string $base_path): ?string {
// Try multiple strategies to detect Flip version.
// Strategy 1: Check the main JS file for version information.
$js_files = [
'/dist/flip.js',
'/dist/flip.min.js',
'/lib/flip.js',
'/src/flip.js',
];
foreach ($js_files as $js_file) {
$file_path = $base_path . $js_file;
if (file_exists($file_path)) {
try {
// Read first 10KB of file.
$handle = fopen($file_path, 'r');
$content = fread($handle, 10240);
fclose($handle);
// Look for version patterns specific to PQINA Flip.
$patterns = [
'/\*\s+@pqina\/flip\s+v?([0-9]+\.[0-9]+(?:\.[0-9]+)?)/',
'/\*\s+Flip\s+v?([0-9]+\.[0-9]+(?:\.[0-9]+)?)/',
'/\*\s+@version\s+([0-9]+\.[0-9]+(?:\.[0-9]+)?)/',
'/version["\']?\s*[:=]\s*["\']([0-9]+\.[0-9]+(?:\.[0-9]+)?)["\']/',
'/Flip\.version\s*=\s*["\']([^"\']+)["\']/',
'/FLIP_VERSION\s*=\s*["\']([0-9]+\.[0-9]+(?:\.[0-9]+)?)["\']/',
];
foreach ($patterns as $pattern) {
if (preg_match($pattern, $content, $matches)) {
$this->logger->info('PQINA Flip version detected: @version from @file', [
'@version' => $matches[1],
'@file' => $js_file,
]);
return $this->normalizeVersion($matches[1]);
}
}
}
catch (\Exception $e) {
$this->logger->error('Error reading Flip file @file: @message', [
'@file' => $js_file,
'@message' => $e->getMessage(),
]);
}
}
}
// Strategy 2: Check CSS file for version comments.
$css_files = [
'/dist/flip.css',
'/dist/flip.min.css',
'/lib/flip.css',
];
foreach ($css_files as $css_file) {
$file_path = $base_path . $css_file;
if (file_exists($file_path)) {
try {
$handle = fopen($file_path, 'r');
$content = fread($handle, 2048);
fclose($handle);
$pattern = '/(?:Flip|@pqina\/flip)\s+v?([0-9]+\.[0-9]+(?:\.[0-9]+)?)/i';
if (preg_match($pattern, $content, $matches)) {
return $this->normalizeVersion($matches[1]);
}
}
catch (\Exception $e) {
$this->logger->warning('Could not read Flip CSS file: @file', [
'@file' => $css_file,
]);
}
}
}
// Strategy 3: Check package.json specifically.
$package_file = $base_path . '/package.json';
if (file_exists($package_file)) {
try {
$content = file_get_contents($package_file);
$package_data = json_decode($content, TRUE);
if (json_last_error() === JSON_ERROR_NONE) {
// Check if it's the right package.
$name = $package_data['name'] ?? '';
if ($name === '@pqina/flip' || $name === 'flip') {
if (!empty($package_data['version'])) {
return $this->normalizeVersion($package_data['version']);
}
}
}
}
catch (\Exception $e) {
$this->logger->warning('Could not read Flip package.json', [
'@error' => $e->getMessage(),
]);
}
}
return NULL;
}
/**
* {@inheritdoc}
*/
public function validateInstallation(string $path): bool {
// First check parent validation.
if (!parent::validateInstallation($path)) {
return FALSE;
}
// Additional Flip-specific validation.
$path = ltrim($path, '/');
$full_path = DRUPAL_ROOT . '/' . $path;
// Check for PQINA Flip-specific indicators.
$flip_indicators = [
'/dist/flip.js',
'/dist/flip.css',
'/lib/flip.js',
'/package.json',
];
$found_indicator = FALSE;
foreach ($flip_indicators as $indicator) {
$check_path = $full_path . $indicator;
if ($indicator === '/package.json' && file_exists($check_path)) {
// Verify it's actually PQINA Flip.
try {
$content = file_get_contents($check_path);
$package = json_decode($content, TRUE);
$name = $package['name'] ?? '';
if ($name === '@pqina/flip' || $name === 'flip') {
$found_indicator = TRUE;
break;
}
}
catch (\Exception $e) {
// Continue checking other indicators.
}
}
elseif (file_exists($check_path)) {
$found_indicator = TRUE;
break;
}
}
if (!$found_indicator) {
$this->logger->warning('PQINA Flip library structure not recognized at @path', [
'@path' => $path,
]);
return FALSE;
}
return TRUE;
}
/**
* {@inheritdoc}
*/
public function getRequiredFiles(): array {
return [
'dist/flip.js',
'dist/flip.css',
];
}
/**
* {@inheritdoc}
*/
public function getAlternativePaths(): array {
return [
[
'lib/flip.js',
'lib/flip.css',
],
[
'build/flip.js',
'build/flip.css',
],
[
'src/flip.js',
'src/flip.css',
],
];
}
/**
* {@inheritdoc}
*/
public function hasExtensions(): bool {
// Flip library doesn't have modular extensions.
return FALSE;
}
/**
* {@inheritdoc}
*/
public function buildAttachments(array $config): array {
// Get the base attachments from parent.
$attachments = parent::buildAttachments($config);
// Check once whether Tick is installed using discovery injected in base.
$tick_installed = $this->libraryDiscovery->isInstalled('tick');
if ($tick_installed) {
$attachments['#attached']['library'][] = 'countdown/tick_flip';
}
return $attachments;
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array &$form, FormStateInterface $form_state, array $default_values = []): void {
// First, build the common fields from parent.
parent::buildConfigurationForm($form, $form_state, $default_values);
// Now add Flip-specific fields to the library_specific fieldset.
// Reorganize the fieldset to be more comprehensive.
$form['library_specific']['#title'] = $this->t('Flip Animation Settings');
$form['library_specific']['#description'] = $this->t('Configure the flip animation and appearance settings.');
// Animation settings group.
$form['library_specific']['animation'] = [
'#type' => 'details',
'#title' => $this->t('Animation'),
'#open' => TRUE,
];
$form['library_specific']['animation']['flip_duration'] = [
'#type' => 'number',
'#title' => $this->t('Flip Duration'),
'#description' => $this->t('Duration of the flip animation in milliseconds.'),
'#default_value' => $this->getConfigValue($default_values, 'flip_duration', 800),
'#min' => 100,
'#max' => 5000,
'#step' => 100,
'#field_suffix' => $this->t('ms'),
];
$form['library_specific']['animation']['flip_easing'] = [
'#type' => 'select',
'#title' => $this->t('Flip Easing'),
'#description' => $this->t('Easing function for the flip animation.'),
'#options' => self::EASING_FUNCTIONS,
'#default_value' => $this->getConfigValue($default_values, 'flip_easing', 'ease-out-bounce'),
];
$form['library_specific']['animation']['shadow_style'] = [
'#type' => 'radios',
'#title' => $this->t('Shadow Style'),
'#description' => $this->t('Choose the shadow effect style.'),
'#options' => [
'default' => $this->t('Default (outer shadows)'),
'inner' => $this->t('Inner shadows only'),
'none' => $this->t('No shadows'),
],
'#default_value' => $this->getConfigValue($default_values, 'shadow_style', 'default'),
];
$form['library_specific']['animation']['rounded_style'] = [
'#type' => 'radios',
'#title' => $this->t('Rounded Corners'),
'#description' => $this->t('Choose the rounded corner style.'),
'#options' => [
'default' => $this->t('Default (card corners)'),
'panels' => $this->t('Panel corners'),
'none' => $this->t('No rounded corners'),
],
'#default_value' => $this->getConfigValue($default_values, 'rounded_style', 'default'),
];
// Display settings group.
$form['library_specific']['display'] = [
'#type' => 'details',
'#title' => $this->t('Display'),
'#open' => TRUE,
];
$format_default = $this->getConfigValue($default_values, 'format', ['d', 'h', 'm', 's']);
$form['library_specific']['display']['format'] = [
'#type' => 'checkboxes',
'#title' => $this->t('Time Units'),
'#description' => $this->t('Select which time units to display.'),
'#options' => [
'y' => $this->t('Years'),
'M' => $this->t('Months'),
'w' => $this->t('Weeks'),
'd' => $this->t('Days'),
'h' => $this->t('Hours'),
'm' => $this->t('Minutes'),
's' => $this->t('Seconds'),
],
'#default_value' => $format_default,
'#required' => TRUE,
];
$form['library_specific']['display']['separator'] = [
'#type' => 'textfield',
'#title' => $this->t('Separator'),
'#description' => $this->t('Character to use between time units.'),
'#default_value' => $this->getConfigValue($default_values, 'separator', ':'),
'#size' => 5,
'#maxlength' => 5,
];
$form['library_specific']['display']['leading_zeros'] = [
'#type' => 'checkbox',
'#title' => $this->t('Leading Zeros'),
'#description' => $this->t('Display leading zeros for single-digit values.'),
'#default_value' => $this->getConfigValue($default_values, 'leading_zeros', TRUE),
];
// Labels settings group.
$form['library_specific']['labels'] = [
'#type' => 'details',
'#title' => $this->t('Labels'),
'#description' => $this->t('Customize the labels for each time unit.'),
'#open' => FALSE,
];
$form['library_specific']['labels']['show_labels'] = [
'#type' => 'checkbox',
'#title' => $this->t('Show Labels'),
'#description' => $this->t('Display labels under each time unit.'),
'#default_value' => $this->getConfigValue($default_values, 'show_labels', TRUE),
];
// Individual label customization.
$default_labels = $this->getConfigValue($default_values, 'labels', [
'years' => 'Years',
'months' => 'Months',
'weeks' => 'Weeks',
'days' => 'Days',
'hours' => 'Hours',
'minutes' => 'Minutes',
'seconds' => 'Seconds',
]);
foreach (['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds'] as $unit) {
$form['library_specific']['labels'][$unit] = [
'#type' => 'textfield',
'#title' => $this->t('@unit Label', ['@unit' => ucfirst($unit)]),
'#default_value' => $default_labels[$unit] ?? ucfirst($unit),
'#size' => 20,
'#states' => [
'visible' => [
':input[name="library_specific[labels][show_labels]"]' => ['checked' => TRUE],
],
],
];
}
// Appearance settings group.
$form['library_specific']['appearance'] = [
'#type' => 'details',
'#title' => $this->t('Appearance'),
'#open' => FALSE,
];
$form['library_specific']['appearance']['theme'] = [
'#type' => 'select',
'#title' => $this->t('Theme'),
'#description' => $this->t('Select a predefined theme or customize.'),
'#options' => [
'dark' => $this->t('Dark'),
'light' => $this->t('Light'),
'auto' => $this->t('Auto (follows system)'),
'custom' => $this->t('Custom'),
],
'#default_value' => $this->getConfigValue($default_values, 'theme', 'dark'),
];
$form['library_specific']['appearance']['size'] = [
'#type' => 'select',
'#title' => $this->t('Size'),
'#description' => $this->t('Select the size of the flip counter.'),
'#options' => [
'xs' => $this->t('Extra Small'),
'sm' => $this->t('Small'),
'md' => $this->t('Medium'),
'lg' => $this->t('Large'),
'xl' => $this->t('Extra Large'),
'responsive' => $this->t('Responsive'),
],
'#default_value' => $this->getConfigValue($default_values, 'size', 'md'),
];
// Custom theme options.
$form['library_specific']['appearance']['font_family'] = [
'#type' => 'select',
'#title' => $this->t('Font Family'),
'#options' => [
'monospace' => $this->t('Monospace'),
'sans-serif' => $this->t('Sans Serif'),
'serif' => $this->t('Serif'),
'system-ui' => $this->t('System UI'),
],
'#default_value' => $this->getConfigValue($default_values, 'font_family', 'monospace'),
'#states' => [
'visible' => [
':input[name="library_specific[appearance][theme]"]' => ['value' => 'custom'],
],
],
];
$form['library_specific']['appearance']['text_color'] = [
'#type' => 'textfield',
'#title' => $this->t('Text Color'),
'#description' => $this->t('CSS color value for text.'),
'#default_value' => $this->getConfigValue($default_values, 'text_color', '#ffffff'),
'#size' => 10,
'#states' => [
'visible' => [
':input[name="library_specific[appearance][theme]"]' => ['value' => 'custom'],
],
],
];
$form['library_specific']['appearance']['background_color'] = [
'#type' => 'textfield',
'#title' => $this->t('Background Color'),
'#description' => $this->t('CSS color value for background.'),
'#default_value' => $this->getConfigValue($default_values, 'background_color', '#333333'),
'#size' => 10,
'#states' => [
'visible' => [
':input[name="library_specific[appearance][theme]"]' => ['value' => 'custom'],
],
],
];
// Advanced settings group.
$form['library_specific']['advanced'] = [
'#type' => 'details',
'#title' => $this->t('Advanced'),
'#open' => FALSE,
];
$form['library_specific']['advanced']['autostart'] = [
'#type' => 'checkbox',
'#title' => $this->t('Auto Start'),
'#description' => $this->t('Start counting automatically when loaded.'),
'#default_value' => $this->getConfigValue($default_values, 'autostart', TRUE),
];
$form['library_specific']['advanced']['responsive'] = [
'#type' => 'checkbox',
'#title' => $this->t('Responsive'),
'#description' => $this->t('Adjust size based on container width.'),
'#default_value' => $this->getConfigValue($default_values, 'responsive', TRUE),
];
$form['library_specific']['advanced']['stop_at_zero'] = [
'#type' => 'checkbox',
'#title' => $this->t('Stop at Zero'),
'#description' => $this->t('Stop counting when reaching zero (countdown only).'),
'#default_value' => $this->getConfigValue($default_values, 'stop_at_zero', TRUE),
];
$form['library_specific']['advanced']['update_interval'] = [
'#type' => 'number',
'#title' => $this->t('Update Interval'),
'#description' => $this->t('How often to update the counter in milliseconds.'),
'#default_value' => $this->getConfigValue($default_values, 'update_interval', 1000),
'#min' => 100,
'#max' => 60000,
'#step' => 100,
'#field_suffix' => $this->t('ms'),
];
$form['library_specific']['advanced']['show_credits'] = [
'#type' => 'checkbox',
'#title' => $this->t('Show Credits'),
'#description' => $this->t('Display "Powered by PQINA" credit link.'),
'#default_value' => $this->getConfigValue($default_values, 'show_credits', FALSE),
];
$form['library_specific']['advanced']['custom_css_class'] = [
'#type' => 'textfield',
'#title' => $this->t('Custom CSS Class'),
'#description' => $this->t('Additional CSS class(es) for the counter wrapper.'),
'#default_value' => $this->getConfigValue($default_values, 'custom_css_class', ''),
];
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state): void {
// First validate common fields.
parent::validateConfigurationForm($form, $form_state);
// Get values from form state.
$values = $form_state->getValues();
// Validate Flip-specific fields.
// Ensure at least one time unit is selected.
$format = array_filter($values['library_specific']['display']['format'] ?? []);
if (empty($format)) {
$form_state->setErrorByName('library_specific][display][format',
$this->t('At least one time unit must be selected.'));
}
// Validate flip duration is within reasonable range.
$flip_duration = $values['library_specific']['animation']['flip_duration'] ?? NULL;
if ($flip_duration !== NULL && ($flip_duration < 100 || $flip_duration > 5000)) {
$form_state->setErrorByName('library_specific][animation][flip_duration',
$this->t('Flip duration must be between 100 and 5000 milliseconds.'));
}
// Validate update interval is within reasonable range.
$update_interval = $values['library_specific']['advanced']['update_interval'] ?? NULL;
if ($update_interval !== NULL && ($update_interval < 100 || $update_interval > 60000)) {
$form_state->setErrorByName('library_specific][advanced][update_interval',
$this->t('Update interval must be between 100 and 60000 milliseconds.'));
}
// Validate color formats if custom theme is selected.
$theme = $values['library_specific']['appearance']['theme'] ?? NULL;
if ($theme === 'custom') {
$text_color = $values['library_specific']['appearance']['text_color'] ?? NULL;
$bg_color = $values['library_specific']['appearance']['background_color'] ?? NULL;
// Validate CSS color format.
$color_pattern = '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$|^rgb\(|^rgba\(|^[a-z]+$/i';
if ($text_color && !preg_match($color_pattern, $text_color)) {
$form_state->setErrorByName('library_specific][appearance][text_color',
$this->t('Text color must be a valid CSS color value.'));
}
if ($bg_color && !preg_match($color_pattern, $bg_color)) {
$form_state->setErrorByName('library_specific][appearance][background_color',
$this->t('Background color must be a valid CSS color value.'));
}
}
// Validate CSS class names.
$css_class = $values['library_specific']['advanced']['custom_css_class'] ?? NULL;
if ($css_class && !preg_match('/^[a-zA-Z0-9\s_-]+$/', $css_class)) {
$form_state->setErrorByName('library_specific][advanced][custom_css_class',
$this->t('CSS class names can only contain letters, numbers, spaces, hyphens, and underscores.'));
}
}
/**
* {@inheritdoc}
*/
public function getDefaultConfiguration(): array {
// Get parent defaults then add Flip-specific defaults.
$defaults = parent::getDefaultConfiguration();
// Animation defaults.
$defaults['flip_duration'] = 800;
$defaults['flip_easing'] = 'ease-out-bounce';
$defaults['shadow_style'] = 'default';
$defaults['rounded_style'] = 'default';
// Display defaults.
$defaults['format'] = ['d', 'h', 'm', 's'];
$defaults['separator'] = ':';
$defaults['leading_zeros'] = TRUE;
// Labels defaults.
$defaults['show_labels'] = TRUE;
$defaults['labels'] = [
'years' => 'Years',
'months' => 'Months',
'weeks' => 'Weeks',
'days' => 'Days',
'hours' => 'Hours',
'minutes' => 'Minutes',
'seconds' => 'Seconds',
];
// Appearance defaults.
$defaults['theme'] = 'dark';
$defaults['size'] = 'md';
$defaults['font_family'] = 'monospace';
$defaults['text_color'] = '#ffffff';
$defaults['background_color'] = '#333333';
// Advanced defaults.
$defaults['autostart'] = TRUE;
$defaults['responsive'] = TRUE;
$defaults['stop_at_zero'] = TRUE;
$defaults['update_interval'] = 1000;
$defaults['show_credits'] = FALSE;
$defaults['custom_css_class'] = '';
return $defaults;
}
/**
* {@inheritdoc}
*/
public function getJavaScriptSettings(array $configuration): array {
// Get parent settings then add Flip-specific settings.
$settings = parent::getJavaScriptSettings($configuration);
// Convert format checkboxes to array of selected values.
$format = isset($configuration['format'])
? array_values(array_filter($configuration['format']))
: ['d', 'h', 'm', 's'];
// Core Flip settings.
$settings['view'] = 'flip';
$settings['format'] = $format;
// Animation settings with proper type casting.
$settings['flipDuration'] = (int) $this->getConfigValue($configuration, 'flip_duration', 800);
$settings['flipEasing'] = $this->getConfigValue($configuration, 'flip_easing', 'ease-out-bounce');
$settings['shadowStyle'] = $this->getConfigValue($configuration, 'shadow_style', 'default');
$settings['roundedStyle'] = $this->getConfigValue($configuration, 'rounded_style', 'default');
// Display settings.
$settings['separator'] = $this->getConfigValue($configuration, 'separator', ':');
$settings['leadingZeros'] = (bool) $this->getConfigValue($configuration, 'leading_zeros', TRUE);
// Labels settings.
$settings['showLabels'] = (bool) $this->getConfigValue($configuration, 'show_labels', TRUE);
$settings['labels'] = $this->getConfigValue($configuration, 'labels', [
'years' => 'Years',
'months' => 'Months',
'weeks' => 'Weeks',
'days' => 'Days',
'hours' => 'Hours',
'minutes' => 'Minutes',
'seconds' => 'Seconds',
]);
// Appearance settings.
$settings['theme'] = $this->getConfigValue($configuration, 'theme', 'dark');
$settings['size'] = $this->getConfigValue($configuration, 'size', 'md');
// Custom theme colors if applicable.
if ($settings['theme'] === 'custom') {
$settings['fontFamily'] = $this->getConfigValue($configuration, 'font_family', 'monospace');
$settings['textColor'] = $this->getConfigValue($configuration, 'text_color', '#ffffff');
$settings['backgroundColor'] = $this->getConfigValue($configuration, 'background_color', '#333333');
}
// Advanced settings with proper type casting.
$settings['autostart'] = (bool) $this->getConfigValue($configuration, 'autostart', TRUE);
$settings['responsive'] = (bool) $this->getConfigValue($configuration, 'responsive', TRUE);
$settings['stopAtZero'] = (bool) $this->getConfigValue($configuration, 'stop_at_zero', TRUE);
$settings['updateInterval'] = (int) $this->getConfigValue($configuration, 'update_interval', 1000);
$settings['showCredits'] = (bool) $this->getConfigValue($configuration, 'show_credits', FALSE);
$settings['customCssClass'] = $this->getConfigValue($configuration, 'custom_css_class', '');
return $settings;
}
}
