From d855d74cdd051d3fe18152aa8ee3f185e838245c Mon Sep 17 00:00:00 2001 From: Stefan Borchert Date: Sat, 4 Jun 2011 00:46:26 +0200 Subject: [PATCH] Issue #992020: Autocomplete caret position. --- includes/common.inc | 12 ++++++++---- misc/autocomplete.js | 6 +++--- modules/taxonomy/taxonomy.pages.inc | 30 +++++++++++++++++++++--------- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/includes/common.inc b/includes/common.inc index 4ec37dc..5cd9ddb 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -7069,17 +7069,21 @@ function drupal_explode_tags($tags) { // This regexp allows the following types of user input: // this, "somecompany, llc", "and ""this"" w,o.rks", foo bar $regexp = '%(?:^|,\ *)("(?>[^"]*)(?>""[^"]* )*"|(?: [^",]*))%x'; - preg_match_all($regexp, $tags, $matches); - $typed_tags = array_unique($matches[1]); + preg_match_all($regexp, $tags, $matches, PREG_OFFSET_CAPTURE); + $typed_tags = array(); + foreach ($matches[1] as $match) { + $typed_tags[$match[1]] = $match[0]; + } + $typed_tags = array_unique($typed_tags); $tags = array(); - foreach ($typed_tags as $tag) { + foreach ($typed_tags as $position => $tag) { // If a user has escaped a term (to demonstrate that it is a group, // or includes a comma or quote character), we remove the escape // formatting so to save the term into the database as the user intends. $tag = trim(str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $tag))); if ($tag != "") { - $tags[] = $tag; + $tags[$position] = $tag; } } diff --git a/misc/autocomplete.js b/misc/autocomplete.js index 5e85be4..e39476c 100644 --- a/misc/autocomplete.js +++ b/misc/autocomplete.js @@ -200,7 +200,7 @@ Drupal.jsAC.prototype.populatePopup = function () { // Do search. this.db.owner = this; - this.db.search(this.input.value); + this.db.search(this.input.value, $input.get(0).selectionStart); }; /** @@ -264,7 +264,7 @@ Drupal.ACDB = function (uri) { /** * Performs a cached and delayed search. */ -Drupal.ACDB.prototype.search = function (searchString) { +Drupal.ACDB.prototype.search = function (searchString, caretPosition) { var db = this; this.searchString = searchString; @@ -290,7 +290,7 @@ Drupal.ACDB.prototype.search = function (searchString) { // Ajax GET request for autocompletion. $.ajax({ type: 'GET', - url: db.uri + '/' + encodeURIComponent(searchString), + url: db.uri + '/' + encodeURIComponent(searchString) + '/' + caretPosition, dataType: 'json', success: function (matches) { if (typeof matches.status == 'undefined' || matches.status != 0) { diff --git a/modules/taxonomy/taxonomy.pages.inc b/modules/taxonomy/taxonomy.pages.inc index 3aed290..2672f81 100644 --- a/modules/taxonomy/taxonomy.pages.inc +++ b/modules/taxonomy/taxonomy.pages.inc @@ -76,15 +76,27 @@ function taxonomy_term_feed($term) { /** * Helper function for autocompletion */ -function taxonomy_autocomplete($field_name, $tags_typed = '') { +function taxonomy_autocomplete($field_name, $tags_typed = '', $caret_position) { $field = field_info_field($field_name); - // The user enters a comma-separated list of tags. We only autocomplete the last tag. - $tags_typed = drupal_explode_tags($tags_typed); - $tag_last = drupal_strtolower(array_pop($tags_typed)); + // The user enters a comma-separated list of tags. + $tags_typed_array = drupal_explode_tags($tags_typed); + // Try to find the tag based on the current caret position. + $tag_current = ''; + $tag_positions = array_keys($tags_typed_array); + $tag_positions[] = drupal_strlen($tags_typed) + 1; + $position_last = -1; + foreach ($tag_positions as $position) { + if ($caret_position >= $position_last && $caret_position < $position) { + $tag_current = $tags_typed_array[$position_last]; + break; + } + $position_last = $position; + } + unset($tags_typed_array[$position_last]); $matches = array(); - if ($tag_last != '') { + if ($tag_current != '') { // Part of the criteria for the query come from the field's own settings. $vids = array(); @@ -98,19 +110,19 @@ function taxonomy_autocomplete($field_name, $tags_typed = '') { $query->addTag('term_access'); // Do not select already entered terms. - if (!empty($tags_typed)) { - $query->condition('t.name', $tags_typed, 'NOT IN'); + if (!empty($tags_typed_array)) { + $query->condition('t.name', $tags_typed_array, 'NOT IN'); } // Select rows that match by term name. $tags_return = $query ->fields('t', array('tid', 'name')) ->condition('t.vid', $vids) - ->condition('t.name', '%' . db_like($tag_last) . '%', 'LIKE') + ->condition('t.name', '%' . db_like($tag_current) . '%', 'LIKE') ->range(0, 10) ->execute() ->fetchAllKeyed(); - $prefix = count($tags_typed) ? implode(', ', $tags_typed) . ', ' : ''; + $prefix = count($tags_typed_array) ? implode(', ', $tags_typed_array) . ', ' : ''; $term_matches = array(); foreach ($tags_return as $tid => $name) { -- 1.7.4