<?php

/**
 * @file
 * General functions and hook implementations.
 *
 * @see http://harvesthq.github.com/chosen/
 */

/**
 * Define chosen library url.
 */
define('CHOSEN_WEBSITE_URL', 'http://harvesthq.github.io/chosen');
define('CHOSEN_INCLUDE_ADMIN', 0);
define('CHOSEN_INCLUDE_NO_ADMIN', 1);
define('CHOSEN_INCLUDE_EVERYWHERE', 2);

/**
 * Implements hook_help().
 */
function chosen_help($path, $arg) {
  switch ($path) {
    case 'admin/help#chosen':
      $output = '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('Chosen uses the Chosen jQuery plugin to make your < select > elements more user-friendly.') . '</p>';
      $output .= '<h3>' . t('Usage') . '</h3>';
      $output .= '<p>' . t('Configure at: <a target="_blank" href="@structure_types">admin/config/user-interface/chosen</a>', array('@structure_types' => base_path() . 'admin/config/user-interface/chosen')) . '</p>';
      return $output;
  }
}

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

  $items['admin/config/user-interface/chosen'] = array(
    'title' => 'Chosen',
    'description' => 'Configuration for Chosen plugin',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('chosen_admin_settings'),
    'access arguments' => array('administer site configuration'),
    'file' => 'chosen.admin.inc',
  );

  return $items;
}

/**
 * Implements hook_field_widget_info_alter().
 *
 * A list of settings needed by Chosen module for widgets.
 */
function chosen_field_widget_info_alter(&$info) {
  $widget_defaults = array(
    'options_select' => '',
    'select_or_other' => '',
    'date_combo' => 0,
  );

  foreach ($widget_defaults as $widget => $default) {
    if (isset($info[$widget])) {
      $info[$widget]['settings']['apply_chosen'] = $default;
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function chosen_form_field_ui_field_edit_form_alter(&$form, $form_state) {
  if (empty($form['#field']['locked']) && isset($form['#instance']['widget']['settings']['apply_chosen'])) {
    $form['instance']['widget']['settings']['apply_chosen'] = array(
      '#type' => 'select',
      '#title' => t('Apply Chosen to the select fields in this widget?'),
      '#options' => array(
        0 => t('Do not apply'),
        1 => t('Apply'),
      ),
      '#default_value' => $form['#instance']['widget']['settings']['apply_chosen'],
      '#disabled' => !chosen_get_chosen_path(),
      '#empty_option' => t('No preference'),
      '#empty_value' => '',
      '#chosen' => FALSE,
    );
  }
}

/**
 * Implements hook_field_widget_form_alter().
 */
function chosen_field_widget_form_alter(&$element, &$form_state, $context) {
  if (isset($context['instance']['widget']['settings']['apply_chosen'])) {
    $value = $context['instance']['widget']['settings']['apply_chosen'];
    if ($value === '') {
      return;
    }
    else {
      $element['#chosen'] = !empty($value);
    }
  }
}

/**
 * Implements hook_library().
 */
function chosen_library() {
  global $theme;

  $library_path = chosen_get_chosen_path();

  $info['chosen'] = array(
    'title' => 'Chosen',
    'website' => CHOSEN_WEBSITE_URL,
    'version' => '1.1.0',
    'js' => array(
      $library_path . '/chosen.jquery.min.js' => array(),
    ),
  );

  $css_disabled_themes = variable_get('chosen_disabled_themes', array());

  // Only add the Chosen CSS if it is not disabled for the active theme.
  if (!in_array($theme, $css_disabled_themes, TRUE)) {
    $css = $library_path . '/chosen.css';
    if (!file_exists($css)) {
      $css = $library_path . '/chosen.min.css';
    }
    $info['chosen']['css'] = array($css => array());
  }

  // All the settings that are actually passed through into the chosen()
  // function are contained in this array.
  $options = array(
    'allow_single_deselect' => (bool) variable_get('chosen_allow_single_deselect', FALSE),
    'disable_search' => (bool) variable_get('chosen_disable_search', FALSE),
    'disable_search_threshold' => (int) variable_get('chosen_disable_search_threshold', 0),
    'search_contains' => (bool) variable_get('chosen_search_contains', FALSE),
    'placeholder_text_multiple' => variable_get('chosen_placeholder_text_multiple', t('Choose some options')),
    'placeholder_text_single' => variable_get('chosen_placeholder_text_single', t('Choose an option')),
    'no_results_text' => variable_get('chosen_no_results_text', t('No results match')),
    'inherit_select_classes' => TRUE,
  );

  $module_path = drupal_get_path('module', 'chosen');
  $info['drupal.chosen'] = array(
    'title' => 'Drupal Chosen integration',
    'website' => 'https://drupal.org/project/chosen',
    'version' => '1.1.0',
    'js' => array(
      $module_path . '/chosen.js' => array(
        'group' => JS_DEFAULT,
        'weight' => 100,
      ),
      array(
        'data' => array(
          'chosen' => array(
            'selector' => variable_get('chosen_jquery_selector', 'select:visible'),
            'minimum_single' => (int) variable_get('chosen_minimum_single', 20),
            'minimum_multiple' => (int) variable_get('chosen_minimum_multiple', 20),
            'minimum_width' => (int) variable_get('chosen_minimum_width', ''),
            'options' => $options,
          ),
        ),
        'type' => 'setting',
      ),
    ),
    'css' => array(
      $module_path . '/css/chosen-drupal.css' => array(),
    ),
    'dependencies' => array(
      array('system', 'jquery.once'),
      array('chosen', 'chosen'),
    ),
  );

  return $info;
}

/**
 * Implements hook_element_info_alter().
 */
function chosen_element_info_alter(&$info) {
  $info['select']['#pre_render'][] = 'chosen_pre_render_select';

  if (module_exists('date')) {
    $info['date_combo']['#pre_render'][] = 'chosen_pre_render_date_combo';
  }

  if (module_exists('select_or_other')) {
    $info['select_or_other']['#pre_render'][] = 'chosen_pre_render_select_or_other';
  }
}

/**
 * Render API callback: Apply Chosen to a select element.
 */
function chosen_pre_render_select($element) {
  // Exclude chosen from theme other than admin.
  global $theme, $language;

  $is_admin = path_is_admin(current_path()) || current_path() == 'system/ajax' || $theme == variable_get('admin_theme');
  $chosen_include = variable_get('chosen_include', CHOSEN_INCLUDE_EVERYWHERE);
  if ($chosen_include != CHOSEN_INCLUDE_EVERYWHERE && $is_admin == $chosen_include) {
    return $element;
  }

  // If the #chosen FAPI property is set, then add the appropriate class.
  if (isset($element['#chosen'])) {
    if (!empty($element['#chosen'])) {
      // Element has opted-in for Chosen, ensure the library gets added.
      $element['#attributes']['class'][] = 'chosen-enable';
    }
    else {
      $element['#attributes']['class'][] = 'chosen-disable';
      // Element has opted-out of Chosen. Do not add the library now.
      return $element;
    }
  }
  elseif (isset($element['#attributes']['class']) && is_array($element['#attributes']['class'])) {
    if (array_intersect($element['#attributes']['class'], array('chosen-disable'))) {
      // Element has opted-out of Chosen. Do not add the library now.
      return $element;
    }
    elseif (array_intersect($element['#attributes']['class'], array('chosen-enable', 'chosen-widget'))) {
      // Element has opted-in for Chosen, ensure the library gets added.
      // @todo Remove support for the deprecated chosen-widget class.
    }
  }
  else {
    // Neither the #chosen property was set, nor any chosen classes found.
    // This element still might match the site-wide critera, so add the library.
  }

  if (isset($element['#field_name']) && !empty($element['#multiple'])) {
    // Remove '_none' from multi-select options.
    unset($element['#options']['_none']);

    $field = field_info_field($element['#field_name']);
    if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && $field['cardinality'] > 1) {
      $element['#attributes']['data-cardinality'] = $field['cardinality'];
    }
  }

  // Right to Left Support.
  if ($language->direction == 1) {
    $element['#attributes']['class'][] = 'chosen-rtl';
  }

  $element['#attached']['library'][] = array('chosen', 'drupal.chosen');
  return $element;
}

/**
 * Render API callback: Apply Chosen to a date_combo element.
 */
function chosen_pre_render_date_combo($element) {
  // Because the date_combo field contains many different select elements, we
  // need to recurse down and apply the FAPI property to each one.
  if (isset($element['#chosen'])) {
    chosen_element_apply_property_recursive($element, $element['#chosen']);
  }
  return $element;
}

/**
 * Render API callback: Apply Chosen to a select_or_other element.
 */
function chosen_pre_render_select_or_other($element) {
  if ($element['#select_type'] == 'select' && isset($element['#chosen'])) {
    $element['select']['#chosen'] = $element['#chosen'];
  }
  return $element;
}

/**
 * Recurse through an element to apply the chosen property to any select fields.
 */
function chosen_element_apply_property_recursive(array &$element, $chosen_value = NULL) {
  if (!isset($chosen_value)) {
    if (isset($element['#chosen'])) {
      $chosen_value = $element['#chosen'];
    }
    else {
      return;
    }
  }
  if (isset($element['#type']) && $element['#type'] == 'select') {
    $element['#chosen'] = $chosen_value;
  }
  foreach (element_children($element) as $key) {
    chosen_element_apply_property_recursive($element[$key], $chosen_value);
  }
}

/**
 * Get the location of the chosen library.
 *
 * @return mixed
 *   The location of the library, or FALSE if the library isn't installed.
 */
function chosen_get_chosen_path() {
  if (function_exists('libraries_get_path')) {
    return libraries_get_path('chosen');
  }

  // The following logic is taken from libraries_get_libraries()
  $searchdir = array();

  // Similar to 'modules' and 'themes' directories inside an installation
  // profile, installation profiles may want to place libraries into a
  // 'libraries' directory.
  $searchdir[] = 'profiles/' . drupal_get_profile() . '/libraries';

  // Always search sites/all/libraries.
  $searchdir[] = 'sites/all/libraries';

  // Also search sites/<domain>/*.
  $searchdir[] = conf_path() . '/libraries';

  foreach ($searchdir as $dir) {
    if (file_exists($dir . '/chosen/chosen.jquery.min.js')) {
      return $dir . '/chosen';
    }
  }

  return FALSE;
}
