506 lines
14 KiB
Plaintext
506 lines
14 KiB
Plaintext
import Utility from './utility'
|
|
import SavedSearch from './saved_searches'
|
|
|
|
let Autocomplete = {};
|
|
|
|
/* eslint-disable */
|
|
Autocomplete.METATAGS = <%= Tag::METATAGS.to_json.html_safe %>;
|
|
Autocomplete.TAG_CATEGORIES = <%= TagCategory.mapping.to_json.html_safe %>;
|
|
Autocomplete.ORDER_METATAGS = <%= Tag::ORDER_METATAGS.to_json.html_safe %>;
|
|
/* eslint-enable */
|
|
|
|
Autocomplete.TAG_PREFIXES = "-|~|" + Object.keys(Autocomplete.TAG_CATEGORIES).map(category => category + ":").join("|");
|
|
Autocomplete.TAG_PREFIXES_REGEX = new RegExp("^(" + Autocomplete.TAG_PREFIXES + ")(.*)$", "i");
|
|
Autocomplete.METATAGS_REGEX = new RegExp("^(" + Autocomplete.METATAGS.join("|") + "):(.*)$", "i");
|
|
|
|
Autocomplete.initialize_all = function() {
|
|
if (Utility.meta("enable-auto-complete") === "true") {
|
|
$.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,
|
|
});
|
|
|
|
this.initialize_tag_autocomplete();
|
|
this.initialize_mention_autocomplete($(".autocomplete-mentions textarea"));
|
|
this.initialize_artist_autocomplete($('[data-autocomplete="artist"]'));
|
|
this.initialize_pool_autocomplete($('[data-autocomplete="pool"]'));
|
|
this.initialize_user_autocomplete($('[data-autocomplete="user"]'));
|
|
this.initialize_wiki_autocomplete($('[data-autocomplete="wiki-page"]'));
|
|
}
|
|
}
|
|
|
|
Autocomplete.initialize_mention_autocomplete = function($fields) {
|
|
$fields.autocomplete({
|
|
search: function() {
|
|
$(this).data("ui-autocomplete").menu.bindings = $();
|
|
},
|
|
select: function(event, ui) {
|
|
Autocomplete.insert_completion(this, ui.item.value);
|
|
return false;
|
|
},
|
|
source: 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) {
|
|
Autocomplete.user_source(name, resp, "@");
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
Autocomplete.initialize_tag_autocomplete = function() {
|
|
var $fields_multiple = $('[data-autocomplete="tag-query"], [data-autocomplete="tag-edit"]');
|
|
var $fields_single = $('[data-autocomplete="tag"]');
|
|
|
|
$fields_multiple.autocomplete({
|
|
search: function() {
|
|
if ($(this).data("ui-autocomplete")) {
|
|
$(this).data("ui-autocomplete").menu.bindings = $();
|
|
}
|
|
},
|
|
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: function(req, resp) {
|
|
var query = Autocomplete.parse_query(req.term, this.element.get(0).selectionStart);
|
|
var metatag = query.metatag;
|
|
var term = query.term;
|
|
|
|
if (!metatag && !term) {
|
|
this.close();
|
|
return;
|
|
} else if (!metatag && term) {
|
|
Autocomplete.normal_source(term, resp);
|
|
return;
|
|
}
|
|
|
|
switch (metatag) {
|
|
case "order":
|
|
case "status":
|
|
case "rating":
|
|
case "locked":
|
|
case "child":
|
|
case "parent":
|
|
case "filetype":
|
|
case "disapproval":
|
|
Autocomplete.static_metatag_source(term, resp, metatag);
|
|
return;
|
|
case "user":
|
|
case "approver":
|
|
case "commenter":
|
|
case "comm":
|
|
case "noter":
|
|
case "noteupdater":
|
|
case "artcomm":
|
|
case "fav":
|
|
case "ordfav":
|
|
case "appealer":
|
|
case "flagger":
|
|
case "upvote":
|
|
case "downvote":
|
|
Autocomplete.user_source(term, resp, metatag + ":");
|
|
break;
|
|
case "pool":
|
|
case "ordpool":
|
|
Autocomplete.pool_source(term, resp, metatag);
|
|
break;
|
|
case "favgroup":
|
|
Autocomplete.favorite_group_source(term, resp, metatag);
|
|
break;
|
|
case "search":
|
|
Autocomplete.saved_search_source(term, resp);
|
|
break;
|
|
default:
|
|
resp([]);
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
$fields_single.autocomplete({
|
|
search: function() {
|
|
$(this).data("ui-autocomplete").menu.bindings = $();
|
|
},
|
|
source: function(req, resp) {
|
|
Autocomplete.normal_source(req.term, resp);
|
|
}
|
|
});
|
|
}
|
|
|
|
Autocomplete.initialize_artist_autocomplete = function($fields) {
|
|
$fields.autocomplete({
|
|
search: function() {
|
|
$(this).data("ui-autocomplete").menu.bindings = $();
|
|
},
|
|
source: function(req, resp) {
|
|
$.ajax({
|
|
url: "/artists.json",
|
|
data: {
|
|
"search[name_like]": req.term.trim().replace(/\s+/g, "_") + "*",
|
|
"search[is_active]": true,
|
|
"search[order]": "post_count",
|
|
"limit": 10,
|
|
"expiry": 7
|
|
},
|
|
method: "get",
|
|
success: function(data) {
|
|
resp($.map(data, function(artist) {
|
|
return {
|
|
type: "tag",
|
|
label: artist.name.replace(/_/g, " "),
|
|
value: artist.name,
|
|
category: Autocomplete.TAG_CATEGORIES.artist,
|
|
};
|
|
}));
|
|
}
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
Autocomplete.initialize_pool_autocomplete = function($fields) {
|
|
$fields.autocomplete({
|
|
search: function() {
|
|
$(this).data("ui-autocomplete").menu.bindings = $();
|
|
},
|
|
source: function(req, resp) {
|
|
Autocomplete.pool_source(req.term, resp);
|
|
},
|
|
});
|
|
};
|
|
|
|
Autocomplete.initialize_user_autocomplete = function($fields) {
|
|
$fields.autocomplete({
|
|
search: function() {
|
|
$(this).data("ui-autocomplete").menu.bindings = $();
|
|
},
|
|
source: function(req, resp) {
|
|
Autocomplete.user_source(req.term, resp, "");
|
|
},
|
|
});
|
|
};
|
|
|
|
Autocomplete.initialize_wiki_autocomplete = function($fields) {
|
|
$fields.autocomplete({
|
|
search: function() {
|
|
$(this).data("ui-autocomplete").menu.bindings = $();
|
|
},
|
|
source: function(req, resp) {
|
|
$.ajax({
|
|
url: "/wiki_pages.json",
|
|
data: {
|
|
"search[title]": req.term + "*",
|
|
"search[hide_deleted]": "Yes",
|
|
"search[order]": "post_count",
|
|
"limit": 10,
|
|
"expiry": 7
|
|
},
|
|
method: "get",
|
|
success: function(data) {
|
|
resp($.map(data, function(wiki_page) {
|
|
return {
|
|
type: "tag",
|
|
label: wiki_page.title.replace(/_/g, " "),
|
|
value: wiki_page.title,
|
|
category: wiki_page.category_name
|
|
};
|
|
}));
|
|
}
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
Autocomplete.normal_source = function(term, resp) {
|
|
return $.ajax({
|
|
url: "/tags/autocomplete.json",
|
|
data: {
|
|
"search[name_matches]": term,
|
|
"expiry": 7
|
|
},
|
|
method: "get",
|
|
success: function(data) {
|
|
var d = $.map(data, function(tag) {
|
|
return {
|
|
type: "tag",
|
|
label: tag.name.replace(/_/g, " "),
|
|
antecedent: tag.antecedent_name,
|
|
value: tag.name,
|
|
category: tag.category,
|
|
source: tag.source,
|
|
weight: tag.weight,
|
|
post_count: tag.post_count
|
|
};
|
|
});
|
|
|
|
resp(d);
|
|
}
|
|
});
|
|
}
|
|
|
|
Autocomplete.parse_query = function(text, caret) {
|
|
var metatag = "";
|
|
var term = "";
|
|
|
|
var before_caret_text = text.substring(0, caret);
|
|
var match = before_caret_text.match(/\S+$/g);
|
|
if (match) {
|
|
term = match[0];
|
|
} else {
|
|
return {};
|
|
}
|
|
|
|
match = term.match(Autocomplete.TAG_PREFIXES_REGEX);
|
|
if (match) {
|
|
metatag = match[1].toLowerCase();
|
|
term = match[2];
|
|
}
|
|
|
|
match = term.match(Autocomplete.METATAGS_REGEX);
|
|
if (match) {
|
|
metatag = match[1].toLowerCase();
|
|
term = match[2];
|
|
}
|
|
|
|
return { metatag: metatag, term: term };
|
|
};
|
|
|
|
// 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) {
|
|
var before_caret_text = input.value.substring(0, input.selectionStart).trim();
|
|
var after_caret_text = input.value.substring(input.selectionStart).trim();
|
|
|
|
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(" → ").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 (item.type === "tag") {
|
|
$link.addClass("tag-type-" + item.category);
|
|
} else if (item.type === "user") {
|
|
var level_class = "user-" + item.level.toLowerCase();
|
|
$link.addClass(level_class);
|
|
if (Utility.meta("style-usernames") === "true") {
|
|
$link.addClass("with-style");
|
|
}
|
|
} 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", "source", "antecedent", "value", "category", "post_count", "weight"];
|
|
data_attributes.forEach(attr => {
|
|
$list_item.attr(`data-autocomplete-${attr.replace(/_/g, "-")}`, item[attr]);
|
|
});
|
|
|
|
return $list_item.appendTo(list);
|
|
};
|
|
|
|
Autocomplete.static_metatags = {
|
|
order: Autocomplete.ORDER_METATAGS,
|
|
status: [
|
|
"any", "deleted", "active", "pending", "flagged", "banned", "modqueue", "unmoderated"
|
|
],
|
|
rating: [
|
|
"safe", "questionable", "explicit"
|
|
],
|
|
locked: [
|
|
"rating", "note", "status"
|
|
],
|
|
child: [
|
|
"any", "none"
|
|
],
|
|
parent: [
|
|
"any", "none"
|
|
],
|
|
filetype: [
|
|
"jpg", "png", "gif", "swf", "zip", "webm", "mp4"
|
|
],
|
|
disapproval: [
|
|
"any", "none", "disinterest", "poor_quality", "breaks_rules"
|
|
]
|
|
}
|
|
|
|
Autocomplete.static_metatag_source = function(term, resp, metatag) {
|
|
var sub_metatags = this.static_metatags[metatag];
|
|
|
|
var matches = sub_metatags.filter(sub_metatag => sub_metatag.startsWith(term.toLowerCase()));
|
|
matches = matches.map(sub_metatag => `${metatag}:${sub_metatag}`).sort().slice(0, 10);
|
|
|
|
resp(matches);
|
|
}
|
|
|
|
Autocomplete.user_source = function(term, resp, prefix) {
|
|
return $.ajax({
|
|
url: "/users.json",
|
|
data: {
|
|
"search[order]": "post_upload_count",
|
|
"search[current_user_first]": "true",
|
|
"search[name_matches]": term + "*",
|
|
"limit": 10
|
|
},
|
|
method: "get",
|
|
success: function(data) {
|
|
resp($.map(data, function(user) {
|
|
return {
|
|
type: "user",
|
|
label: user.name.replace(/_/, " "),
|
|
value: prefix + user.name,
|
|
level: user.level_string
|
|
};
|
|
}));
|
|
}
|
|
});
|
|
}
|
|
|
|
Autocomplete.pool_source = function(term, resp, metatag) {
|
|
return $.ajax({
|
|
url: "/pools.json",
|
|
data: {
|
|
"search[order]": "post_count",
|
|
"search[name_matches]": term,
|
|
"search[is_deleted]": false,
|
|
"limit": 10
|
|
},
|
|
method: "get",
|
|
success: function(data) {
|
|
resp($.map(data, function(pool) {
|
|
return {
|
|
type: "pool",
|
|
label: pool.name.replace(/_/g, " "),
|
|
value: (metatag ? (metatag + ":" + pool.name) : pool.name),
|
|
post_count: pool.post_count,
|
|
category: pool.category
|
|
};
|
|
}));
|
|
}
|
|
});
|
|
}
|
|
|
|
Autocomplete.favorite_group_source = function(term, resp, metatag) {
|
|
return $.ajax({
|
|
url: "/favorite_groups.json",
|
|
data: {
|
|
"search[name_matches]": term,
|
|
"limit": 10
|
|
},
|
|
method: "get",
|
|
success: function(data) {
|
|
resp($.map(data, function(favgroup) {
|
|
return {
|
|
label: favgroup.name.replace(/_/g, " "),
|
|
value: metatag + ":" + favgroup.name,
|
|
post_count: favgroup.post_count
|
|
};
|
|
}));
|
|
}
|
|
});
|
|
}
|
|
|
|
Autocomplete.saved_search_source = function(term, resp) {
|
|
return SavedSearch.labels(term).then(function(labels) {
|
|
resp(labels.map(function(label) {
|
|
return {
|
|
label: label.replace(/_/g, " "),
|
|
value: "search:" + label,
|
|
};
|
|
}));
|
|
});
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
Autocomplete.initialize_all();
|
|
});
|
|
|
|
export default Autocomplete;
|
|
|