/**
  * Global #process callback for form elements.
  *
  * @param array $element
  *   The element render array.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  * @param array $complete_form
  *   The complete form structure.
  *
  * @return array
  *   The altered element array.
  *
  * @see \Drupal\materialize\Plugin\Alter\ElementInfo::alter
  */
 public static function process(array $element, FormStateInterface $form_state, array &$complete_form)
 {
     if (!empty($element['#materialize_ignore_process'])) {
         return $element;
     }
     static $theme;
     if (!isset($theme)) {
         $theme = Materialize::getTheme();
     }
     $e = Element::create($element, $form_state);
     // Process AJAX.
     if ($e->getProperty('ajax') && !$e->isButton() || $e->getProperty('autocomplete_route_name')) {
         static::processAjax($e, $form_state, $complete_form);
     }
     // Add "form-inline" class.
     if ($e->hasClass('container-inline')) {
         $e->replaceClass('container-inline', 'form-inline');
     }
     if ($e->isType(['color', 'date', 'number', 'range', 'tel', 'weight'])) {
         $e->addClass('form-inline', 'wrapper_attributes');
     }
     // Check for errors and set the "has_error" property flag.
     $errors = $e->getError();
     $e->setProperty('errors', $errors);
     if (isset($errors) || $e->getProperty('required') && $theme->getSetting('forms_required_has_error')) {
         $e->setProperty('has_error', TRUE);
     }
     // Process input groups.
     if ($e->getProperty('input') && ($e->getProperty('input_group') || $e->getProperty('input_group_button'))) {
         static::processInputGroups($e, $form_state, $complete_form);
     }
     return $element;
 }
 /**
  * Extracts the hook name from a function name.
  *
  * @param string $string
  *   The function name to extract the hook name from.
  * @param string $suffix
  *   A suffix hook ending (like "alter") to also remove.
  * @param string $prefix
  *   A prefix hook beginning (like "form") to also remove.
  *
  * @return string
  *   The extracted hook name.
  */
 public static function extractHook($string, $suffix = NULL, $prefix = NULL)
 {
     $regex = '^(' . implode('|', array_keys(Materialize::getTheme()->getAncestry())) . ')';
     $regex .= $prefix ? '_' . $prefix : '';
     $regex .= $suffix ? '_|_' . $suffix . '$' : '';
     return preg_replace("/{$regex}/", '', $string);
 }
 /**
  * {@inheritdoc}
  */
 public function __construct(array $configuration, $plugin_id, $plugin_definition)
 {
     if (!isset($configuration['theme'])) {
         $configuration['theme'] = Materialize::getTheme();
     }
     $this->theme = $configuration['theme'];
     parent::__construct($configuration, $plugin_id, $plugin_definition);
 }
 /**
  * Retrieves the currently selected theme on the settings form.
  *
  * @param array $form
  *   Nested array of form elements that comprise the form.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  *
  * @return \Drupal\materialize\Theme|FALSE
  *   The currently selected theme object or FALSE if not a Materialize theme.
  */
 public static function getTheme(array &$form, FormStateInterface $form_state)
 {
     $build_info = $form_state->getBuildInfo();
     $theme = isset($build_info['args'][0]) ? Materialize::getTheme($build_info['args'][0]) : FALSE;
     // Do not continue if the theme is not Materialize specific.
     if (!$theme || !$theme->subthemeOf('materialize')) {
         unset($form['#submit'][0]);
         unset($form['#validate'][0]);
     }
     return $theme;
 }
 /**
  * {@inheritdoc}
  */
 public function __construct(array $configuration, $plugin_id, $plugin_definition)
 {
     // This is technically a plugin constructor, but because we wish to use the
     // protected methods of the Registry class, we must extend from it. Thus,
     // to properly construct the extended Registry object, we must pass the
     // arguments it would normally get from the service container to "fake" it.
     if (!isset($configuration['theme'])) {
         $configuration['theme'] = Materialize::getTheme();
     }
     $this->currentTheme = $configuration['theme'];
     parent::__construct(\Drupal::service('app.root'), \Drupal::service('cache.default'), \Drupal::service('lock'), \Drupal::service('module_handler'), \Drupal::service('theme_handler'), \Drupal::service('theme.initialization'), $this->currentTheme->getName());
     $this->setThemeManager(\Drupal::service('theme.manager'));
     $this->init();
 }
 /**
  * Retrieves the full base/sub-theme ancestry of a theme.
  *
  * @param bool $reverse
  *   Whether or not to return the array of themes in reverse order, where the
  *   active theme is the first entry.
  *
  * @return \Drupal\bootstrap\Theme[]
  *   An associative array of \Drupal\bootstrap objects (theme), keyed
  *   by machine name.
  */
 public function getAncestry($reverse = FALSE)
 {
     $ancestry = $this->themeHandler->getBaseThemes($this->themes, $this->getName());
     foreach (array_keys($ancestry) as $name) {
         $ancestry[$name] = Materialize::getTheme($name, $this->themeHandler);
     }
     $ancestry[$this->getName()] = $this;
     return $reverse ? array_reverse($ancestry) : $ancestry;
 }
 /**
  * Converts an element description into a tooltip based on certain criteria.
  *
  * @param array|\Drupal\materialize\Utility\Element|NULL $target_element
  *   The target element render array the tooltip is to be attached to, passed
  *   by reference or an existing Element object. If not set, it will default
  *   this Element instance.
  * @param bool $input_only
  *   Toggle determining whether or not to only convert input elements.
  * @param int $length
  *   The length of characters to determine if description is "simple".
  *
  * @return $this
  */
 public function smartDescription(&$target_element = NULL, $input_only = TRUE, $length = NULL)
 {
     static $theme;
     if (!isset($theme)) {
         $theme = Materialize::getTheme();
     }
     // Determine if tooltips are enabled.
     static $enabled;
     if (!isset($enabled)) {
         $enabled = $theme->getSetting('tooltip_enabled') && $theme->getSetting('forms_smart_descriptions');
     }
     // Immediately return if tooltip descriptions are not enabled.
     if (!$enabled) {
         return $this;
     }
     // Allow a different element to attach the tooltip.
     /** @var Element $target */
     if (is_object($target_element) && $target_element instanceof self) {
         $target = $target_element;
     } elseif (isset($target_element) && is_array($target_element)) {
         $target = new self($target_element, $this->formState);
     } else {
         $target = $this;
     }
     // Retrieve the length limit for smart descriptions.
     if (!isset($length)) {
         // Disable length checking by setting it to FALSE if empty.
         $length = (int) $theme->getSetting('forms_smart_descriptions_limit') ?: FALSE;
     }
     // Retrieve the allowed tags for smart descriptions. This is primarily used
     // for display purposes only (i.e. non-UI/UX related elements that wouldn't
     // require a user to "click", like a link). Disable length checking by
     // setting it to FALSE if empty.
     static $allowed_tags;
     if (!isset($allowed_tags)) {
         $allowed_tags = array_filter(array_unique(array_map('trim', explode(',', $theme->getSetting('forms_smart_descriptions_allowed_tags') . '')))) ?: FALSE;
     }
     // Return if element or target shouldn't have "simple" tooltip descriptions.
     $html = FALSE;
     if ($input_only && !$target->hasProperty('input') || !$this->getProperty('smart_description', TRUE) || !$target->getProperty('smart_description', TRUE) || !$this->hasProperty('description') || $target->hasAttribute('data-toggle') || !Unicode::isSimple($this->getProperty('description'), $length, $allowed_tags, $html)) {
         return $this;
     }
     // Default attributes type.
     $type = DrupalAttributes::ATTRIBUTES;
     // Use #label_attributes for 'checkbox' and 'radio' elements.
     if ($this->isType(['checkbox', 'radio'])) {
         $type = DrupalAttributes::LABEL;
     } elseif ($this->isType(['checkboxes', 'radios'])) {
         $type = DrupalAttributes::WRAPPER;
     }
     // Retrieve the proper attributes array.
     $attributes = $target->getAttributes($type);
     // Set the tooltip attributes.
     $attributes['title'] = $allowed_tags !== FALSE ? Xss::filter((string) $this->getProperty('description'), $allowed_tags) : $this->getProperty('description');
     $attributes['data-toggle'] = 'tooltip';
     if ($html || $allowed_tags === FALSE) {
         $attributes['data-html'] = 'true';
     }
     // Remove the element description so it isn't (re-)rendered later.
     $this->unsetProperty('description');
     return $this;
 }