You are here

slide_with_style.module in Select with Style 7

Defines a slider RANGE widget to create filters for numeric and list fields.

File

slide_with_style/slide_with_style.module
View source
<?php

/**
 * @file
 * Defines a slider RANGE widget to create filters for numeric and list fields.
 */

/**
 * Implements hook_help().
 */
function slide_with_style_help($path, $arg) {
  switch ($path) {
    case 'admin/help#slide_with_style':
      $t = t('Configuration and usage instructions are in this <a href="@README">README</a> file.<br/>Known issues and solutions may be found on the <a href="@select_with_style">Select with Style</a> project page.', array(
        '@README' => url(drupal_get_path('module', 'slide_with_style') . '/README.txt'),
        '@select_with_style' => url('http://drupal.org/project/select_with_style'),
      ));
      break;
  }
  return empty($t) ? '' : '<p>' . $t . '</p>';
}

/**
 * Implements hook_menu().
 */
function slide_with_style_menu() {
  $items = array();

  // Put the administrative settings under System on the Configuration page.
  $items['admin/config/system/slide_with_style'] = array(
    'title' => 'Slide with Style',
    'description' => 'Configure Slide with Style module.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'slide_with_style_admin_configure',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'file' => 'slide_with_style.admin.inc',
  );
  return $items;
}

/**
 * Implements hook_field_widget_info().
 */
function slide_with_style_field_widget_info() {

  // @todo dynamically build list types, including contrib modules
  $number_types = array(
    'number_decimal',
    'number_float',
    'number_integer',
  );
  $list_types = array(
    'list_text',
    'list_integer',
    'list_float',
  );
  $widget_info = array(
    'slide_with_style_slider' => array(
      'label' => t('Slider'),
      'field types' => array_merge($number_types, $list_types),
      'settings' => array(
        'step' => 1,
        'appearance' => array(
          'with_textfield' => 'with_textfield',
          'with_bubble' => 'with_bubble',
        ),
        'css file' => '',
      ),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
        'default value' => FIELD_BEHAVIOR_DEFAULT,
      ),
    ),
  );
  return $widget_info;
}

/**
 * Implements hook_field_widget_form().
 *
 * Creates a textfield with CSS class for the JS to attach the slider to.
 */
function slide_with_style_field_widget_form(&$form, &$form_state, $field, $instance, $lang, $items, $delta, $element) {
  switch ($field['module']) {
    case 'list':
      $additions = array(
        'value' => array(
          '#title' => $instance['label'],
          '#type' => 'textfield',
          '#default_value' => isset($items[$delta]) ? reset($items[$delta]) : NULL,
          '#required' => !empty($element['#required']),
          '#size' => 5,
        ),
      );
      break;
    default:

      // Use the default numeric field widget as a base.
      $additions = number_field_widget_form($form, $form_state, $field, $instance, $lang, $items, $delta, $element);

      // When used as a range, this field is read-only
      $additions['value']['#size'] = min(8, $additions['value']['#size']);
  }

  // All additions are in $element['value']
  $element += $additions;

  // Define the class through which the javascript will find this textfield.
  $element['value']['#attributes']['class'][] = "edit-slide-with-style-slider";

  // Append to the textfield a placeholder for the slider
  $element['value']['#suffix'] = '<div class="slide-with-style-slider"></div>';
  if (empty($form_state['slider_id'])) {
    $has_multiple_values = field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT;

    // id needs to match up with the element id generated by core for this input element.
    $parent_id = '';
    if (!empty($element['#field_parents'])) {
      $parent_id .= trim($element['#field_parents'][0], '_ ') . '-';
      if (!empty($element['#field_parents'][1])) {
        $parent_id .= trim($element['#field_parents'][1], '_ ') . '-';
      }
    }
    $base_id = 'edit-' . $parent_id . trim($instance['field_name'], '_ ') . "-{$lang}-";
    $id = $base_id . ($has_multiple_values ? "{$delta}-value" : 'value');
  }
  else {
    $id = $form_state['slider_id'];
  }

  // Pass jQueryUI slider parameters via the js settings.
  $widget_settings = $instance['widget']['settings'];
  $text_values = FALSE;
  if ($field['module'] == 'number') {
    $min = $instance['settings']['min'];
    $max = $instance['settings']['max'];
  }
  elseif ($field['module'] == 'list') {
    if (!empty($field['settings']['allowed_values'])) {
      $text_values = $field['settings']['allowed_values'];
      $keys = array_keys($text_values);
      $min = reset($keys);
      $max = array_pop($keys);
    }
  }

  // Do not use $element['value']['#default_value'] -- it is the same as $items,
  // but may be formatted.
  if (isset($items[$delta]['value'])) {
    $initial_value = $items[$delta]['value'];
  }
  elseif (isset($instance['default_value'][0])) {
    $initial_value = $instance['default_value'][0]['value'];
  }
  else {
    $initial_value = $min;
  }
  $slider_parameters = array(
    drupal_html_class($id) => array(
      'step' => empty($widget_settings['step']) ? 1 : $widget_settings['step'],
      'min' => $min,
      'max' => $max,
      'value' => $initial_value,
      'values' => empty($element['#default_values']) ? NULL : $element['#default_values'],
      'textfield' => !empty($widget_settings['appearance']['with_textfield']),
      'textvalues' => $text_values,
      'bubble' => !empty($widget_settings['appearance']['with_bubble']),
      'orientation' => empty($widget_settings['appearance']['vertical']) ? 'horizontal' : 'vertical',
      'range' => empty($element['#default_values']) ? 'min' : TRUE,
      'autosubmit' => !empty($element['#autosubmit']),
    ),
  );

  // Start by attaching settings or it will break global_filter_field_slider_widget_form_alter()
  $element['value']['#attached']['js'][] = array(
    'data' => array(
      'slider' => $slider_parameters,
    ),
    'type' => 'setting',
    'scope' => 'header',
  );
  $path = drupal_get_path('module', 'slide_with_style');
  $element['value']['#attached']['js'][] = $path . '/slide_with_style.js';
  $element['value']['#attached']['css'][] = $path . '/slide_with_style.css';
  $css_files = slide_with_style_css_files();
  if (!empty($css_files[$widget_settings['css file']])) {
    $element['value']['#attached']['css'][] = $css_files[$widget_settings['css file']];
  }
  $element['value']['#attached']['library'][] = array(
    'system',
    'ui.slider',
  );

  // Just in case we need to do more stuff after the form has been built...
  $element['value']['#process'][] = 'slide_with_style_field_widget_process';
  return $element;
}

/**
 * Element #process callback retains slider value in case of validation errors.
 *
 * Without this a validation error elsewhere on the form will result in the
 * slider falling back to the value it had when the form was first loaded.
 */
function slide_with_style_field_widget_process($element, &$form_state, $form) {
  $parents = $element['#parents'];
  foreach ($element['#attached']['js'][0]['data']['slider'] as &$slider_parameters) {

    // Loops only once. PS: Surely there's a more elegant way...
    if (!empty($form_state['values'][$parents[0]]) && is_array($form_state['values'][$parents[0]]) && !empty($form_state['values'][$parents[0]][$parents[1]][$parents[2]][$parents[3]])) {
      $slider_parameters['value'] = $form_state['values'][$parents[0]][$parents[1]][$parents[2]][$parents[3]];
    }
  }
  return $element;
}

/**
 * Implements hook_field_widget_settings_form().
 */
function slide_with_style_field_widget_settings_form($field, $instance) {
  $settings = $instance['widget']['settings'];
  switch ($instance['widget']['type']) {
    case 'slide_with_style_slider':
      $form['appearance'] = array(
        '#type' => 'checkboxes',
        '#title' => t('Slider appearance'),
        '#default_value' => $settings['appearance'],
        '#options' => array(
          'with_textfield' => t('with synchronised text field'),
          'with_bubble' => t('with value bubble'),
          'vertical' => t('vertical instead of horizontal'),
        ),
      );
      if ($field['module'] == 'number') {
        $form['step'] = array(
          '#type' => 'textfield',
          '#title' => t('Step'),
          '#default_value' => $settings['step'],
          '#description' => t('The increment that the slider should snap to.'),
          '#element_validate' => array(
            '_element_validate_number',
          ),
          '#required' => TRUE,
        );
      }
      $options = array(
        '' => 'core (no styling)',
      );
      foreach (slide_with_style_css_files() as $name => $filespec) {
        $options[$name] = $name;
      }
      $form['css file'] = array(
        '#type' => 'select',
        '#multiple' => FALSE,
        '#size' => 1,
        '#title' => t('Additional slider styling file'),
        '#default_value' => $settings['css file'],
        '#options' => $options,
        '#description' => t('The directory where the above files are looked up may be changed on the Slide with Style <a href="@href">configuraton page</a>.', array(
          '@href' => url('admin/config/system/slide_with_style'),
        )),
      );
      break;
  }
  return $form;
}

/**
 * Implements hook_field_widget_error().
 *
 * @see number_field_widget_validate()
 * @see form_error()
 */
function slide_with_style_field_widget_error($element, $error, $form, &$form_state) {
  form_error($element, $error['message']);
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Make min/max required when using any of the slider widgets.
 */
function slide_with_style_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
  if ($form['#instance']['widget']['type'] == 'slide_with_style_slider') {
    $form['instance']['settings']['min']['#required'] = TRUE;
    $form['instance']['settings']['max']['#required'] = TRUE;
  }
}

/**
 * Returns an array of CSS files in a directory.
 *
 * @param $dir
 *   A stream wrapper URI that is a directory or NULL to use the directory
 *   set on the configuration page.
 *
 * @return
 *   An array of full CSS file names in the supplied directory.
 */
function slide_with_style_css_files($dir = NULL) {
  if (empty($dir)) {
    $default_path = drupal_get_path('module', 'slide_with_style') . '/css';
    $path = variable_get('slide_with_style_css_directory', $default_path);
    $dirname = DRUPAL_ROOT . "/{$path}";
  }
  else {

    // not tested, not used
    $dirname = file_stream_wrapper_uri_normalize($dir);
  }
  $files = array();
  if ($all_files = @scandir($dirname)) {
    foreach ($all_files as $filename) {
      if (drupal_substr($filename, -4) == '.css' && is_file("{$dirname}/{$filename}")) {
        $files[$filename] = "{$path}/{$filename}";
      }
    }
  }
  return $files;
}

/**
 * Return a list of all instances belonging to the supplied field.
 *
 * @param string $field_name
 *   Name of the field whose instances are to be returned.
 * @return array
 *   Array of instances belonging to the specified field.
 */
function slide_with_style_get_field_instances($field_name) {
  $instances = array();
  foreach (field_info_instances() as $type_bundles) {
    foreach ($type_bundles as $bundle_instances) {
      foreach ($bundle_instances as $fld_name => $instance) {
        if ($fld_name == $field_name) {
          $instances[] = $instance;
        }
      }
    }
  }
  return $instances;
}

/**
 * Implements hook_views_api().
 */
function slide_with_style_views_api() {
  return array(
    'api' => views_api_version(),
    'path' => drupal_get_path('module', 'slide_with_style') . '/views',
  );
}