docker: avoid rebuilding CSS/JS assets on every commit.
Restructure the Dockerfile and the CSS/JS files so that we only rebuild the CSS and JS when they change, not on every commit. Before it took several minutes to rebuild the Docker image after every commit, even when the JS/CSS files didn't change. This also made pulling images slower. This requires refactoring the CSS and JS to not use embedded Ruby (ERB) templates, since this made the CSS and JS dependent on the Ruby codebase, which is why we had to rebuild the assets after every Ruby change.
This commit is contained in:
215
app/javascript/src/javascripts/autocomplete.js
Normal file
215
app/javascript/src/javascripts/autocomplete.js
Normal file
@@ -0,0 +1,215 @@
|
||||
let Autocomplete = {};
|
||||
|
||||
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().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) {
|
||||
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 (/^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) {
|
||||
if (query === "") {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $.getJSON("/autocomplete.json", {
|
||||
"search[query]": query,
|
||||
"search[type]": type,
|
||||
"limit": Autocomplete.MAX_RESULTS
|
||||
});
|
||||
}
|
||||
|
||||
Autocomplete.tag_prefixes = function() {
|
||||
return JSON.parse($("meta[name=autocomplete-tag-prefixes]").attr("content"));
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
Autocomplete.initialize_all();
|
||||
});
|
||||
|
||||
export default Autocomplete;
|
||||
|
||||
Reference in New Issue
Block a user