Skip to content
maskedinput.module 9.36 KiB
Newer Older
Helior Colorado's avatar
Helior Colorado committed
<?php

/**
 * @file
 *  Provides a form element, Field widget, and simple API for using the Masked
 * Input jQuery plugin.
Helior Colorado's avatar
Helior Colorado committed
 */

/**
 * Implements hook_menu().
 */
function maskedinput_menu() {
  $items['admin/config/user-interface/maskedinput'] = array(
    'title' => 'Masked Input',
    'description' => 'Settings page for Masked Input',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('maskedinput_settings'),
    'access arguments' => array('administer site configuration'),
    'file' => 'maskedinput.admin.inc',
    'type' => MENU_NORMAL_ITEM,
  );
Helior Colorado's avatar
Helior Colorado committed
  return $items;
}

/**
 * Implements hook_theme().
 */
function maskedinput_theme($existing, $type, $theme, $path) {
  return array(
    'maskedinput_settings_definitions' => array(
      'render element' => 'form',
      'file' => 'maskedinput.admin.inc',
    ),
  );
}

/**
 * Implements hook_libraries_info().
Helior Colorado's avatar
Helior Colorado committed
 */
function maskedinput_libraries_info() {
Helior Colorado's avatar
Helior Colorado committed
  return array(
    'maskedinput' => array(
      'name' => 'Masked Input',
      'vendor url' => 'http://digitalbush.com/projects/masked-input-plugin',
      'download url' => 'http://cloud.github.com/downloads/digitalBush',
      'versions' => array(
        '1.3' => array(
          'variants' => array(
            'minified' => array(
              'files' => array(
                'js' => array('jquery.maskedinput-1.3.min.js'),
              ),
            ),
            'source' => array(
              'files' => array(
                'js' => array('jquery.maskedinput-1.3.js'),
              ),
            ),
          ),
          'integration files' => array(
            'maskedinput' => array(
              'js' => array('js/maskedinput.js'),
            ),
          ),
Helior Colorado's avatar
Helior Colorado committed
        ),
      ),
    ),
  );
}

/**
 * Implements hook_element_info().
 */
function maskedinput_element_info() {
  $types['maskedinput'] = array(
    '#input' => TRUE,
    '#size' => 60,
    '#maxlength' => 128,
    '#autocomplete_path' => FALSE,
    '#process' => array('maskedinput_process_callback', 'ajax_process_form'),
    '#theme' => 'textfield',
    '#theme_wrappers' => array('form_element'),
    '#mask' => '',
    '#placeholder' => '_',
    '#definitions' => array(),
  );
Helior Colorado's avatar
Helior Colorado committed
  return $types;
}

/**
 * Process callback: 'maskedinput' element type.
 */
function maskedinput_process_callback($element, &$form_state, $form) {
  $info = element_info('maskedinput');
Helior Colorado's avatar
Helior Colorado committed
  // Merge configured definitions with the ones supplied by the form builder.
  if (isset($element['#definitions']) && is_array($element['#definitions'])) {
Helior Colorado's avatar
Helior Colorado committed
    $data['maskedinput']['definitions'] = array_merge(maskedinput_get_configured_definitions(), $element['#definitions']);
  }
Helior Colorado's avatar
Helior Colorado committed
  // Send Drupal.settings a reference to this form element.
  $data['maskedinput']['elements'][$element['#id']] = array(
    'id' => $element['#id'],
    'mask' => isset($element['#mask']) ? $element['#mask'] : $info['#mask'],
    'placeholder' => isset($element['#placeholder']) ? $element['#placeholder'] : $info['#placeholder'],
  );

  // Attaching library, integration script, and settings array.
  $element['#attached']['js'][] = libraries_get_path('maskedinput') . '/jquery.maskedinput-1.3.js';
  $element['#attached']['js'][] = drupal_get_path('module','maskedinput') . '/js/maskedinput.js';
Helior Colorado's avatar
Helior Colorado committed
  $element['#attached']['js'][] = array(
    'type' => 'setting',
Helior Colorado's avatar
Helior Colorado committed
  return $element;
}

/**
 * Implements hook_field_widget_info().
 */
function maskedinput_field_widget_info() {
  // Use defaults in element_info to ensure element alters take affect here too.
Helior Colorado's avatar
Helior Colorado committed
  $element = element_info('maskedinput');

  return array(
    'maskedinput' => array(
      'label' => t('Masked Input'),
      'field types' => array('text', 'text_long', 'number_integer', 'number_decimal', 'number_float'),
Helior Colorado's avatar
Helior Colorado committed
      'settings' => array(
        'size' => 60,
        'mask' => isset($element['#mask']) ? $element['#mask'] : '',
        'placeholder' => isset($element['#placeholder']) ? $element['#placeholder'] : '',
Helior Colorado's avatar
Helior Colorado committed
      ),
    ),
  );
}

/**
 * Implements hook_field_widget_form().
 */
function maskedinput_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $element['value'] = $element + array(
     '#type' => 'maskedinput',
     '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
     '#size' => $instance['widget']['settings']['size'],
     '#mask' => $instance['widget']['settings']['mask'],
     '#placeholder' => $instance['widget']['settings']['placeholder'],
  // Use the textarea html element for "text_long" field types.
  if ($field['type'] == 'text_long') {
    $element['value']['#theme'] = 'textarea';
  }
  // Clean input for field types that expect numeric values
  if (in_array($field['type'], array('number_integer', 'number_decimal', 'number_float'))) {
    $element['value']['#element_validate'][] = 'maskedinput_validate_numeric';
  }
Helior Colorado's avatar
Helior Colorado committed
  return $element;
}

/**
 * Implements hook_field_widget_error().
 */
function maskedinput_field_widget_error($element, $error, $form, &$form_state) {
  form_error($element['value'], $error['message']);
}

Helior Colorado's avatar
Helior Colorado committed
/**
 * Implements hook_field_widget_settings_form().
 */
function maskedinput_field_widget_settings_form($field, $instance) {
  $settings = $instance['widget']['settings'];
  $definitions = maskedinput_view_configured_definitions();
Helior Colorado's avatar
Helior Colorado committed

  $form['size'] = array(
    '#type' => 'textfield',
    '#title' => t('Size of textfield'),
    '#size' => '3',
    '#default_value' => $settings['size'],
    '#element_validate' => array('_element_validate_integer_positive'),
    '#required' => TRUE,
  );
Helior Colorado's avatar
Helior Colorado committed
  $form['mask'] = array(
    '#type' => 'textfield',
    '#title' => t('Mask'),
    '#description' => '',
    '#default_value' => $settings['mask'],
  );
  $form['mask']['#description'] .= t('A mask is defined by a format made up of mask literals and mask definitions. Any character not in the definitions list below is considered a mask literal. Mask literals will be automatically entered for the user as they type and will not be able to be removed by the user.') . ' ';
  $form['mask']['#description'] .= t('Here is a list of definitions that already exist, you can create more at !link', array('!link' => l('admin/config/user-interface/maskedinput', 'admin/config/user-interface/maskedinput', array('query' => array('destination' => $_GET['q'])))));
  $form['mask']['#description'] .= render($definitions);
Helior Colorado's avatar
Helior Colorado committed
  $form['placeholder'] = array(
    '#type' => 'textfield',
    '#title' => t('Placeholder'),
    '#description' => t('Optionally, if you are not satisfied with the underscore ("_") character as a placeholder, you may pass an optional argument to the maskedinput method.'),
    '#default_value' => $settings['placeholder'],
  );
Helior Colorado's avatar
Helior Colorado committed
  return $form;
}

/**
 * Element validation callback for maskedinput elements that require values to
 * be stored as "numeric"
 */
function maskedinput_validate_numeric($element, &$form_state) {
  $existing = NULL;
  $value = drupal_array_get_nested_value($form_state['values'], $element['#parents'], $existing);
  // If the value in question cannot be recognized as being numeric, we strip
  // away everything except numbers and decimals.
  if (!is_numeric($value) && $existing) {
    $clean_value = preg_replace('/[^0-9\.]/u', '', $value);
    form_set_value($element, $clean_value, $form_state);
  }
}

Helior Colorado's avatar
Helior Colorado committed
/**
 * Attach the masked input to existing form elements.
 * 
 * This will ensure we properly merge process callbacks and not break essential
 * functionality from the original form element.
Helior Colorado's avatar
Helior Colorado committed
 */
function maskedinput_attach_to(&$element) {
  $current = element_info($element['#type']) + array('#process' => array());
  $info = element_info('maskedinput') + array('#process' => array());
  $element['#process'] = array_unique(array_merge($current['#process'], $info['#process']));
}

/**
 * Retrieve configured definitions prepared for the #definitions property of
 * the maskedinput element type.
 */
function maskedinput_get_configured_definitions() {
  $definitions = array();
  foreach (variable_get('maskedinput_definitions', array()) as $definition) {
    $definitions[$definition['character']] = $definition['regex'];
  }
Helior Colorado's avatar
Helior Colorado committed
  return $definitions;
}

/**
 * Returns a build to render a preview of available mask definitions as a table.
 */
function maskedinput_view_configured_definitions() {
  // Get default masks.
  $rows = _maskedinput_default_definitions();
Helior Colorado's avatar
Helior Colorado committed
  // Get configured masks.
  foreach (variable_get('maskedinput_definitions', array()) as $definition) {
    $rows[] = array(
      array('data' => $definition['character']),
      array('data' => $definition['regex']),
      array('data' => $definition['description']),
    );
  }
Helior Colorado's avatar
Helior Colorado committed
  $header = array(
    t('Character'),
    t('Regular expression'),
    t('Description'),
  );
Helior Colorado's avatar
Helior Colorado committed
  $build = array(
    '#theme' => 'table__maskedinput_definitions',
    '#header' => $header,
    '#rows' => $rows,
  );
Helior Colorado's avatar
Helior Colorado committed
  return $build;
}

/**
 * Default mask definitions provided by the plugin. Prepared specifically for
 * 'rows' variable of theme_table().
Helior Colorado's avatar
Helior Colorado committed
 */
function _maskedinput_default_definitions() {
  return array(
    array(
      array('data' => 'a'),
      array('data' => '[a-zA-Z]'),
      array('data' => 'Represents an alpha character'),
    ),
    array(
      array('data' => '9'),
      array('data' => '[0-9]'),
      array('data' => 'Represents a numeric character'),
    ),
    array(
      array('data' => '*'),
      array('data' => '[A-Za-z0-9]'),
      array('data' => 'Represents an alpha character'),
    ),
  );
}