Files
danbooru/app/javascript/src/javascripts/autocomplete.js.erb
evazion d408ccbd41 users: remove option to disable autocomplete.
This option was originally added in issue #1747. But only ~350 users
ever disabled autocomplete, only ~120 of these were seen in the last
year, and only 9 new users who signed up in the last year disabled it.

Users wishing to disable autocomplete can use this CSS:

    .ui-autocomplete { display: none !important: }

or this Javascript:

    $("[data-autocomplete]").autocomplete("disable");
2021-01-15 02:03:54 -06:00

215 lines
6.8 KiB
Plaintext

import CurrentUser from './current_user'
let Autocomplete = {};
/* eslint-disable */
Autocomplete.TAG_CATEGORIES = <%= TagCategory.mapping.to_json.html_safe %>;
/* eslint-enable */
Autocomplete.TAG_PREFIXES = "-|~|" + Object.keys(Autocomplete.TAG_CATEGORIES).map(category => category + ":").join("|");
Autocomplete.MAX_RESULTS = 10;
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) {
// Prevent Upload.initialize_enter_on_tags from running if the
// Enter key is used to select a tag from the autocomplete menu.
if (event.key === "Enter") {
event.stopImmediatePropagation();
}
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 match = query.substring(0, caret).match(/\S*$/);
return match[0];
};
// 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 + ")?\\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) {
var $link = $("<a/>");
$link.text(item.label);
$link.attr("href", "/posts?tags=" + encodeURIComponent(item.value));
$link.on("click.danbooru", function(e) {
e.preventDefault();
});
if (item.antecedent) {
var antecedent = item.antecedent.replace(/_/g, " ");
var arrow = $("<span/>").html(" &rarr; ").addClass("autocomplete-arrow");
var antecedent_element = $("<span/>").text(antecedent).addClass("autocomplete-antecedent");
$link.prepend([
antecedent_element,
arrow
]);
}
if (item.post_count !== undefined) {
var count = item.post_count;
if (count >= 1000) {
count = Math.floor(count / 1000) + "k";
}
var $post_count = $("<span/>").addClass("post-count").css("float", "right").text(count);
$link.append($post_count);
}
if (/^tag/.test(item.type)) {
$link.addClass("tag-type-" + item.category);
} else if (item.type === "user") {
var level_class = "user-" + item.level.toLowerCase();
$link.addClass(level_class);
} else if (item.type === "pool") {
$link.addClass("pool-category-" + item.category);
}
var $menu_item = $("<div/>").append($link);
var $list_item = $("<li/>").data("item.autocomplete", item).append($menu_item);
var data_attributes = ["type", "antecedent", "value", "category", "post_count"];
data_attributes.forEach(attr => {
$list_item.attr(`data-autocomplete-${attr.replace(/_/g, "-")}`, item[attr]);
});
return $list_item.appendTo(list);
};
Autocomplete.autocomplete_source = function(query, type) {
return $.getJSON("/autocomplete.json", {
"search[query]": query,
"search[type]": type,
"limit": Autocomplete.MAX_RESULTS
});
}
$(document).ready(function() {
Autocomplete.initialize_all();
});
export default Autocomplete;