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;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc