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:
evazion
2021-10-12 23:03:08 -05:00
parent 587a9d0c8f
commit 206a4b5de5
17 changed files with 56 additions and 70 deletions

View 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(" &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) {
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;