chosen.module 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. <?php
  2. /**
  3. * @file
  4. * General functions and hook implementations.
  5. *
  6. * @see http://harvesthq.github.com/chosen/
  7. */
  8. /**
  9. * Define chosen library url.
  10. */
  11. define('CHOSEN_WEBSITE_URL', 'http://harvesthq.github.io/chosen');
  12. define('CHOSEN_INCLUDE_ADMIN', 0);
  13. define('CHOSEN_INCLUDE_NO_ADMIN', 1);
  14. define('CHOSEN_INCLUDE_EVERYWHERE', 2);
  15. /**
  16. * Implements hook_help().
  17. */
  18. function chosen_help($path, $arg) {
  19. switch ($path) {
  20. case 'admin/help#chosen':
  21. $output = '<h3>' . t('About') . '</h3>';
  22. $output .= '<p>' . t('Chosen uses the Chosen jQuery plugin to make your < select > elements more user-friendly.') . '</p>';
  23. $output .= '<h3>' . t('Usage') . '</h3>';
  24. $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>';
  25. return $output;
  26. }
  27. }
  28. /**
  29. * Implements hook_menu().
  30. */
  31. function chosen_menu() {
  32. $items = array();
  33. $items['admin/config/user-interface/chosen'] = array(
  34. 'title' => 'Chosen',
  35. 'description' => 'Configuration for Chosen plugin',
  36. 'page callback' => 'drupal_get_form',
  37. 'page arguments' => array('chosen_admin_settings'),
  38. 'access arguments' => array('administer site configuration'),
  39. 'file' => 'chosen.admin.inc',
  40. );
  41. return $items;
  42. }
  43. /**
  44. * Implements hook_field_widget_info_alter().
  45. *
  46. * A list of settings needed by Chosen module for widgets.
  47. */
  48. function chosen_field_widget_info_alter(&$info) {
  49. $widget_defaults = array(
  50. 'options_select' => '',
  51. 'select_or_other' => '',
  52. 'date_combo' => 0,
  53. );
  54. foreach ($widget_defaults as $widget => $default) {
  55. if (isset($info[$widget])) {
  56. $info[$widget]['settings']['apply_chosen'] = $default;
  57. }
  58. }
  59. }
  60. /**
  61. * Implements hook_form_FORM_ID_alter().
  62. */
  63. function chosen_form_field_ui_field_edit_form_alter(&$form, $form_state) {
  64. if (empty($form['#field']['locked']) && isset($form['#instance']['widget']['settings']['apply_chosen'])) {
  65. $form['instance']['widget']['settings']['apply_chosen'] = array(
  66. '#type' => 'select',
  67. '#title' => t('Apply Chosen to the select fields in this widget?'),
  68. '#options' => array(
  69. 0 => t('Do not apply'),
  70. 1 => t('Apply'),
  71. ),
  72. '#default_value' => $form['#instance']['widget']['settings']['apply_chosen'],
  73. '#disabled' => !chosen_get_chosen_path(),
  74. '#empty_option' => t('No preference'),
  75. '#empty_value' => '',
  76. '#chosen' => FALSE,
  77. );
  78. }
  79. }
  80. /**
  81. * Implements hook_field_widget_form_alter().
  82. */
  83. function chosen_field_widget_form_alter(&$element, &$form_state, $context) {
  84. if (isset($context['instance']['widget']['settings']['apply_chosen'])) {
  85. $value = $context['instance']['widget']['settings']['apply_chosen'];
  86. if ($value === '') {
  87. return;
  88. }
  89. else {
  90. $element['#chosen'] = !empty($value);
  91. }
  92. }
  93. }
  94. /**
  95. * Implements hook_library().
  96. */
  97. function chosen_library() {
  98. global $theme;
  99. $library_path = chosen_get_chosen_path();
  100. $info['chosen'] = array(
  101. 'title' => 'Chosen',
  102. 'website' => CHOSEN_WEBSITE_URL,
  103. 'version' => '1.1.0',
  104. 'js' => array(
  105. $library_path . '/chosen.jquery.min.js' => array(),
  106. ),
  107. );
  108. $css_disabled_themes = variable_get('chosen_disabled_themes', array());
  109. // Only add the Chosen CSS if it is not disabled for the active theme.
  110. if (!in_array($theme, $css_disabled_themes, TRUE)) {
  111. $css = $library_path . '/chosen.css';
  112. if (!file_exists($css)) {
  113. $css = $library_path . '/chosen.min.css';
  114. }
  115. $info['chosen']['css'] = array($css => array());
  116. }
  117. // All the settings that are actually passed through into the chosen()
  118. // function are contained in this array.
  119. $options = array(
  120. 'allow_single_deselect' => (bool) variable_get('chosen_allow_single_deselect', FALSE),
  121. 'disable_search' => (bool) variable_get('chosen_disable_search', FALSE),
  122. 'disable_search_threshold' => (int) variable_get('chosen_disable_search_threshold', 0),
  123. 'search_contains' => (bool) variable_get('chosen_search_contains', FALSE),
  124. 'placeholder_text_multiple' => variable_get('chosen_placeholder_text_multiple', t('Choose some options')),
  125. 'placeholder_text_single' => variable_get('chosen_placeholder_text_single', t('Choose an option')),
  126. 'no_results_text' => variable_get('chosen_no_results_text', t('No results match')),
  127. 'inherit_select_classes' => TRUE,
  128. );
  129. $module_path = drupal_get_path('module', 'chosen');
  130. $info['drupal.chosen'] = array(
  131. 'title' => 'Drupal Chosen integration',
  132. 'website' => 'https://drupal.org/project/chosen',
  133. 'version' => '1.1.0',
  134. 'js' => array(
  135. $module_path . '/chosen.js' => array(
  136. 'group' => JS_DEFAULT,
  137. 'weight' => 100,
  138. ),
  139. array(
  140. 'data' => array(
  141. 'chosen' => array(
  142. 'selector' => variable_get('chosen_jquery_selector', 'select:visible'),
  143. 'minimum_single' => (int) variable_get('chosen_minimum_single', 20),
  144. 'minimum_multiple' => (int) variable_get('chosen_minimum_multiple', 20),
  145. 'minimum_width' => (int) variable_get('chosen_minimum_width', ''),
  146. 'options' => $options,
  147. ),
  148. ),
  149. 'type' => 'setting',
  150. ),
  151. ),
  152. 'css' => array(
  153. $module_path . '/css/chosen-drupal.css' => array(),
  154. ),
  155. 'dependencies' => array(
  156. array('system', 'jquery.once'),
  157. array('chosen', 'chosen'),
  158. ),
  159. );
  160. return $info;
  161. }
  162. /**
  163. * Implements hook_element_info_alter().
  164. */
  165. function chosen_element_info_alter(&$info) {
  166. $info['select']['#pre_render'][] = 'chosen_pre_render_select';
  167. if (module_exists('date')) {
  168. $info['date_combo']['#pre_render'][] = 'chosen_pre_render_date_combo';
  169. }
  170. if (module_exists('select_or_other')) {
  171. $info['select_or_other']['#pre_render'][] = 'chosen_pre_render_select_or_other';
  172. }
  173. }
  174. /**
  175. * Render API callback: Apply Chosen to a select element.
  176. */
  177. function chosen_pre_render_select($element) {
  178. // Exclude chosen from theme other than admin.
  179. global $theme, $language;
  180. $is_admin = path_is_admin(current_path()) || current_path() == 'system/ajax' || $theme == variable_get('admin_theme');
  181. $chosen_include = variable_get('chosen_include', CHOSEN_INCLUDE_EVERYWHERE);
  182. if ($chosen_include != CHOSEN_INCLUDE_EVERYWHERE && $is_admin == $chosen_include) {
  183. return $element;
  184. }
  185. // If the #chosen FAPI property is set, then add the appropriate class.
  186. if (isset($element['#chosen'])) {
  187. if (!empty($element['#chosen'])) {
  188. // Element has opted-in for Chosen, ensure the library gets added.
  189. $element['#attributes']['class'][] = 'chosen-enable';
  190. }
  191. else {
  192. $element['#attributes']['class'][] = 'chosen-disable';
  193. // Element has opted-out of Chosen. Do not add the library now.
  194. return $element;
  195. }
  196. }
  197. elseif (isset($element['#attributes']['class']) && is_array($element['#attributes']['class'])) {
  198. if (array_intersect($element['#attributes']['class'], array('chosen-disable'))) {
  199. // Element has opted-out of Chosen. Do not add the library now.
  200. return $element;
  201. }
  202. elseif (array_intersect($element['#attributes']['class'], array('chosen-enable', 'chosen-widget'))) {
  203. // Element has opted-in for Chosen, ensure the library gets added.
  204. // @todo Remove support for the deprecated chosen-widget class.
  205. }
  206. }
  207. else {
  208. // Neither the #chosen property was set, nor any chosen classes found.
  209. // This element still might match the site-wide critera, so add the library.
  210. }
  211. if (isset($element['#field_name']) && !empty($element['#multiple'])) {
  212. // Remove '_none' from multi-select options.
  213. unset($element['#options']['_none']);
  214. $field = field_info_field($element['#field_name']);
  215. if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && $field['cardinality'] > 1) {
  216. $element['#attributes']['data-cardinality'] = $field['cardinality'];
  217. }
  218. }
  219. // Right to Left Support.
  220. if ($language->direction == 1) {
  221. $element['#attributes']['class'][] = 'chosen-rtl';
  222. }
  223. $element['#attached']['library'][] = array('chosen', 'drupal.chosen');
  224. return $element;
  225. }
  226. /**
  227. * Render API callback: Apply Chosen to a date_combo element.
  228. */
  229. function chosen_pre_render_date_combo($element) {
  230. // Because the date_combo field contains many different select elements, we
  231. // need to recurse down and apply the FAPI property to each one.
  232. if (isset($element['#chosen'])) {
  233. chosen_element_apply_property_recursive($element, $element['#chosen']);
  234. }
  235. return $element;
  236. }
  237. /**
  238. * Render API callback: Apply Chosen to a select_or_other element.
  239. */
  240. function chosen_pre_render_select_or_other($element) {
  241. if ($element['#select_type'] == 'select' && isset($element['#chosen'])) {
  242. $element['select']['#chosen'] = $element['#chosen'];
  243. }
  244. return $element;
  245. }
  246. /**
  247. * Recurse through an element to apply the chosen property to any select fields.
  248. */
  249. function chosen_element_apply_property_recursive(array &$element, $chosen_value = NULL) {
  250. if (!isset($chosen_value)) {
  251. if (isset($element['#chosen'])) {
  252. $chosen_value = $element['#chosen'];
  253. }
  254. else {
  255. return;
  256. }
  257. }
  258. if (isset($element['#type']) && $element['#type'] == 'select') {
  259. $element['#chosen'] = $chosen_value;
  260. }
  261. foreach (element_children($element) as $key) {
  262. chosen_element_apply_property_recursive($element[$key], $chosen_value);
  263. }
  264. }
  265. /**
  266. * Get the location of the chosen library.
  267. *
  268. * @return mixed
  269. * The location of the library, or FALSE if the library isn't installed.
  270. */
  271. function chosen_get_chosen_path() {
  272. if (function_exists('libraries_get_path')) {
  273. return libraries_get_path('chosen');
  274. }
  275. // The following logic is taken from libraries_get_libraries()
  276. $searchdir = array();
  277. // Similar to 'modules' and 'themes' directories inside an installation
  278. // profile, installation profiles may want to place libraries into a
  279. // 'libraries' directory.
  280. $searchdir[] = 'profiles/' . drupal_get_profile() . '/libraries';
  281. // Always search sites/all/libraries.
  282. $searchdir[] = 'sites/all/libraries';
  283. // Also search sites/<domain>/*.
  284. $searchdir[] = conf_path() . '/libraries';
  285. foreach ($searchdir as $dir) {
  286. if (file_exists($dir . '/chosen/chosen.jquery.min.js')) {
  287. return $dir . '/chosen';
  288. }
  289. }
  290. return FALSE;
  291. }