diff -u b/autocomplete.inc b/autocomplete.inc --- b/autocomplete.inc +++ b/autocomplete.inc @@ -17,7 +17,7 @@ 'autocomplete_items' => '', 'autocomplete_existing' => 0, 'autocomplete_taxonomy' => 0, - 'autocomplete_taxonomy_chosen' => 0, + 'autocomplete_taxonomy_preload' => WEBFORM_AUTOCOMPLETE_PRELOAD_NONE, 'autocomplete_match_rule' => WEBFORM_AUTOCOMPLETE_MATCH_CONTAINS, 'autocomplete_restrict' => 0, 'autocomplete_result_count' => 20, @@ -125,24 +125,29 @@ '#parents' => array('extra', 'autocomplete_taxonomy'), ); + $preload_options = array( + WEBFORM_AUTOCOMPLETE_PRELOAD_NONE => t('None'), + WEBFORM_AUTOCOMPLETE_PRELOAD_NATIVE => t('Use native HTML5, works well on both desktop and mobile'), + ); if (module_exists('chosen') && chosen_get_chosen_path()) { - $chosen_args = array('@chosen_link' => 'https://github.com/harvesthq/chosen'); - $form['options']['autocomplete_taxonomy_chosen'] = array( - '#type' => 'checkbox', - '#title' => t('Use Chosen instead of AJAX'), - '#description' => t('When checked all options are included on form load and selection is handled client side using the Chosen JS library.', $chosen_args), - '#weight' => 1, - '#default_value' => $component['extra']['autocomplete_taxonomy_chosen'], - '#parents' => array('extra', 'autocomplete_taxonomy_chosen'), - '#states' => array( - 'invisible' => array( - 'select[name="extra[autocomplete_taxonomy]"]' => array( - 'value' => 0, - ), + $preload_options[WEBFORM_AUTOCOMPLETE_PRELOAD_CHOSEN] = t('Use Chosen, works great on desktop, reverts to a dropdown on mobile'); + } + $form['options']['autocomplete_taxonomy_preload'] = array( + '#type' => 'radios', + '#title' => t('Preload strategy'), + '#description' => t('If a preload strategy should be used for the autocomplete field. Preloads all possible values for the field so the autocomplete is very fast for the user as there is no need for calls to the Drupal back-end.'), + '#weight' => 1, + '#options' => $preload_options, + '#default_value' => $component['extra']['autocomplete_taxonomy_preload'], + '#parents' => array('extra', 'autocomplete_taxonomy_preload'), + '#states' => array( + 'invisible' => array( + 'select[name="extra[autocomplete_taxonomy]"]' => array( + 'value' => 0, ), ), - ); - } + ), + ); } $form['options']['autocomplete_match_rule'] = array( '#type' => 'select', @@ -249,12 +254,13 @@ $element['#default_value'] = $value[0]; } - // Convert to select element and load all taxonomy terms when chosen is enabled. + // Convert to a preloaded autocomplete element. + // Load all taxonomy terms on page render and remove the need for AJAX + // autocomplete. if ( $component['extra']['autocomplete_taxonomy'] - && $component['extra']['autocomplete_taxonomy_chosen'] + && $component['extra']['autocomplete_taxonomy_preload'] && module_exists('taxonomy') - && module_exists('chosen') ) { $query = new EntityFieldQuery(); $query->entityCondition('entity_type', 'taxonomy_term') @@ -264,15 +270,16 @@ $options = array(); if (isset($result['taxonomy_term'])) { - if (count($result['taxonomy_term']) > WEBFORM_AUTOCOMPLETE_CHOSEN_LIMIT) { - $result['taxonomy_term'] = array_slice($result['taxonomy_term'], 0, WEBFORM_AUTOCOMPLETE_CHOSEN_LIMIT, TRUE); + if (count($result['taxonomy_term']) > WEBFORM_AUTOCOMPLETE_PRELOAD_LIMIT) { + $result['taxonomy_term'] = array_slice($result['taxonomy_term'], 0, WEBFORM_AUTOCOMPLETE_PRELOAD_LIMIT, TRUE); $limit_url = '/node/' . $node->nid . '/webform/components/' . $component['cid']; $limit_link = l('Edit component', $limit_url); - watchdog('webform_autocomplete', 'Using Chosen autocomplete using a taxonomy list larger than 10 000. Results are trimmed to 10 000 to prevent performance issues. Disable Chosen for this component or reduce the list of terms.', array(), WATCHDOG_WARNING, $limit_link); - // If the user can do something about it, warn them about the overly large result set. + watchdog('webform_autocomplete', 'Using autocomplete preload using a taxonomy list larger than 10 000. Results are trimmed to 10 000 to prevent performance issues. Disable preload for this component or reduce the list of terms.', array(), WATCHDOG_WARNING, $limit_link); + // If the user can do something about it, warn them about the overly + // large result set. if (node_access('update', $node)) { $limit_args = array('@component_link' => $limit_url); - drupal_set_message(t('The autocomplete field with Chosen has too many Taxonomy terms. Lists larger than 10 000 can cause performance issues, results are trimmed to 10 000. Disable Chosen for this component or reduce the list of terms.', $limit_args), 'warning'); + drupal_set_message(t('The autocomplete field with preload has too many Taxonomy terms. Lists larger than 10 000 can cause performance issues, results are trimmed to 10 000. Disable preload for this component or reduce the list of terms.', $limit_args), 'warning'); } } @@ -287,11 +294,52 @@ drupal_alter('webform_autocomplete_options', $options, $node, $component['cid'], $str); unset($element['autocomplete_path']); - $element['#type'] = 'select'; - $element['#options'] = $options; - // Set empty empty_option to let Chosen manage the "select something" message. - $element['#empty_option'] = ''; - $element['#chosen'] = TRUE; + if ((int) $component['extra']['autocomplete_taxonomy_preload'] === WEBFORM_AUTOCOMPLETE_PRELOAD_CHOSEN && module_exists('chosen')) { + $element['#type'] = 'select'; + $element['#options'] = $options; + // Set empty empty_option to let Chosen manage the "select something" message. + $element['#empty_option'] = ''; + $element['#chosen'] = TRUE; + } + elseif ((int) $component['extra']['autocomplete_taxonomy_preload'] === WEBFORM_AUTOCOMPLETE_PRELOAD_NATIVE) { + $element['#type'] = 'container'; + $list_key = check_plain($component['form_key']); + $input_id = $list_key . '-input'; + $list_id = $list_key . '-list'; + // Set element id because it is reflected in the label webform creates. + $element['#id'] = $input_id; + $element['input'] = array( + '#type' => 'html_tag', + '#tag' => 'input', + '#attributes' => array( + 'id' => $input_id, + 'list' => $list_id, + 'name' => $list_key, + ) + $element['#attributes'], + ); + $element['datalist'] = array( + '#type' => 'html_tag', + '#tag' => 'datalist', + '#attributes' => array( + 'id' => $list_id, + ), + '#value' => implode("\n", array_map(static function ($item) { + return '