Lowercase searches clientside before sending them to the server. This is to improve cache hit rates when users accidentally type in uppercase, especially on mobile where the first letter often gets capitalized.
176 lines
5.6 KiB
JavaScript
176 lines
5.6 KiB
JavaScript
let Autocomplete = {};
|
|
|
|
Autocomplete.VERSION = 1; // This should be bumped whenever the /autocomplete API changes in order to invalid client caches.
|
|
Autocomplete.MAX_RESULTS = 20;
|
|
|
|
Autocomplete.initialize_all = function() {
|
|
$.widget("ui.autocomplete", $.ui.autocomplete, {
|
|
options: {
|
|
delay: 0,
|
|
minLength: 1,
|
|
autoFocus: false,
|
|
focus: function() { return false; },
|
|
},
|
|
_create: function() {
|
|
this.element.on("keydown.Autocomplete.tab", null, "tab", Autocomplete.on_tab);
|
|
this._super();
|
|
},
|
|
_renderItem: Autocomplete.render_item,
|
|
search: function(value, event) {
|
|
if ($(this).data("ui-autocomplete")) {
|
|
$(this).data("ui-autocomplete").menu.bindings = $();
|
|
}
|
|
this._super(value, event);
|
|
},
|
|
});
|
|
|
|
this.initialize_tag_autocomplete();
|
|
this.initialize_mention_autocomplete($("form div.input.dtext textarea"));
|
|
this.initialize_fields($('[data-autocomplete="tag"]'), "tag");
|
|
this.initialize_fields($('[data-autocomplete="artist"]'), "artist");
|
|
this.initialize_fields($('[data-autocomplete="pool"]'), "pool");
|
|
this.initialize_fields($('[data-autocomplete="user"]'), "user");
|
|
this.initialize_fields($('[data-autocomplete="wiki-page"]'), "wiki_page");
|
|
this.initialize_fields($('[data-autocomplete="favorite-group"]'), "favorite_group");
|
|
this.initialize_fields($('[data-autocomplete="saved-search-label"]'), "saved_search_label");
|
|
}
|
|
|
|
Autocomplete.initialize_fields = function($fields, type) {
|
|
$fields.autocomplete({
|
|
source: async function(request, respond) {
|
|
let results = await Autocomplete.autocomplete_source(request.term, type);
|
|
respond(results);
|
|
},
|
|
});
|
|
};
|
|
|
|
Autocomplete.initialize_mention_autocomplete = function($fields) {
|
|
$fields.autocomplete({
|
|
select: function(event, ui) {
|
|
Autocomplete.insert_completion(this, ui.item.value);
|
|
return false;
|
|
},
|
|
source: async function(req, resp) {
|
|
var cursor = this.element.get(0).selectionStart;
|
|
var name = null;
|
|
|
|
for (var i = cursor; i >= 1; --i) {
|
|
if (req.term[i - 1] === " ") {
|
|
return;
|
|
}
|
|
|
|
if (req.term[i - 1] === "@") {
|
|
if (i === 1 || /[ \r\n]/.test(req.term[i - 2])) {
|
|
name = req.term.substring(i, cursor);
|
|
break;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (name) {
|
|
let results = await Autocomplete.autocomplete_source(name, "mention");
|
|
resp(results);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
Autocomplete.initialize_tag_autocomplete = function() {
|
|
var $fields_multiple = $('[data-autocomplete="tag-query"], [data-autocomplete="tag-edit"]');
|
|
|
|
$fields_multiple.autocomplete({
|
|
select: function(event, ui) {
|
|
Autocomplete.insert_completion(this, ui.item.value);
|
|
return false;
|
|
},
|
|
source: async function(req, resp) {
|
|
let term = Autocomplete.current_term(this.element);
|
|
let results = await Autocomplete.autocomplete_source(term, "tag_query");
|
|
resp(results);
|
|
}
|
|
});
|
|
}
|
|
|
|
Autocomplete.current_term = function($input) {
|
|
let query = $input.get(0).value;
|
|
let caret = $input.get(0).selectionStart;
|
|
let regexp = new RegExp(`^[-~(]*(${Autocomplete.tag_prefixes().join("|")})?`);
|
|
let match = query.substring(0, caret).match(/\S*$/)[0].replace(regexp, "").toLowerCase();
|
|
return match;
|
|
};
|
|
|
|
// Update the input field with the item currently focused in the
|
|
// autocomplete menu, then position the caret just after the inserted completion.
|
|
Autocomplete.insert_completion = function(input, completion) {
|
|
// Trim all whitespace (tabs, spaces) except for line returns
|
|
var before_caret_text = input.value.substring(0, input.selectionStart).replace(/^[ \t]+|[ \t]+$/gm, "");
|
|
var after_caret_text = input.value.substring(input.selectionStart).replace(/^[ \t]+|[ \t]+$/gm, "");
|
|
|
|
var regexp = new RegExp(`([-~(]*(?:${Autocomplete.tag_prefixes().join("|")})?)\\S+$`, "g");
|
|
before_caret_text = before_caret_text.replace(regexp, "$1") + completion + " ";
|
|
|
|
input.value = before_caret_text + after_caret_text;
|
|
input.selectionStart = input.selectionEnd = before_caret_text.length;
|
|
};
|
|
|
|
// If we press tab while the autocomplete menu is open but nothing is
|
|
// focused, complete the first item and close the menu.
|
|
Autocomplete.on_tab = function(event) {
|
|
var input = this;
|
|
var autocomplete = $(input).autocomplete("instance");
|
|
var $autocomplete_menu = autocomplete.menu.element;
|
|
|
|
if (!$autocomplete_menu.is(":visible")) {
|
|
return;
|
|
}
|
|
|
|
if ($autocomplete_menu.has(".ui-state-active").length === 0) {
|
|
var $first_item = $autocomplete_menu.find(".ui-menu-item").first();
|
|
var completion = $first_item.data().uiAutocompleteItem.value;
|
|
|
|
Autocomplete.insert_completion(input, completion);
|
|
autocomplete.close();
|
|
}
|
|
|
|
// Prevent the tab key from moving focus to the next element.
|
|
event.preventDefault();
|
|
};
|
|
|
|
Autocomplete.render_item = function(list, item) {
|
|
item.html.data("ui-autocomplete-item", item);
|
|
return list.append(item.html);
|
|
};
|
|
|
|
Autocomplete.autocomplete_source = async function(query, type) {
|
|
if (query === "") {
|
|
return [];
|
|
}
|
|
|
|
let html = await $.get("/autocomplete", {
|
|
"search[query]": query,
|
|
"search[type]": type,
|
|
"version": Autocomplete.VERSION,
|
|
"limit": Autocomplete.MAX_RESULTS
|
|
});
|
|
|
|
let items = $(html).find("li").toArray().map(item => {
|
|
let $item = $(item);
|
|
return { value: $item.attr("data-autocomplete-value"), html: $item };
|
|
});
|
|
|
|
return items;
|
|
}
|
|
|
|
Autocomplete.tag_prefixes = function() {
|
|
return JSON.parse($("meta[name=autocomplete-tag-prefixes]").attr("content"));
|
|
};
|
|
|
|
$(document).ready(function() {
|
|
Autocomplete.initialize_all();
|
|
});
|
|
|
|
export default Autocomplete;
|
|
|