js: reorganize Javascript file structure.

Move Javascript files from app/components/**/*.js back to app/javascript/src/javascripts/*.js.
This way Javascript files are in one place, which simplifies import paths and makes it
easier to see all Javascript at once.
This commit is contained in:
evazion
2022-02-08 13:43:50 -06:00
parent 37ad6f5a71
commit ef0d8151d8
11 changed files with 15 additions and 16 deletions

View File

@@ -29,26 +29,25 @@ require("@fortawesome/fontawesome-free/css/regular.css");
importAll(require.context('../src/javascripts', true, /\.js(\.erb)?$/));
importAll(require.context('../src/styles', true, /\.s?css(?:\.erb)?$/));
importAll(require.context('../../components', true, /\.js(\.erb)?$/));
importAll(require.context('../../components', true, /\.s?css(?:\.erb)?$/));
import Autocomplete from "../src/javascripts/autocomplete.js";
import Blacklist from "../src/javascripts/blacklists.js";
import CommentComponent from "../../components/comment_component/comment_component.js";
import CommentVotesTooltipComponent from "../../components/comment_votes_tooltip_component/comment_votes_tooltip_component.js";
import CommentComponent from "../src/javascripts/comment_component.js";
import CommentVotesTooltipComponent from "../src/javascripts/comment_votes_tooltip_component.js";
import CurrentUser from "../src/javascripts/current_user.js";
import Dtext from "../src/javascripts/dtext.js";
import FavoritesTooltipComponent from "../../components/favorites_tooltip_component/favorites_tooltip_component.js";
import FileUploadComponent from "../../components/file_upload_component/file_upload_component.js";
import ForumPostComponent from "../../components/forum_post_component/forum_post_component.js";
import FavoritesTooltipComponent from "../src/javascripts/favorites_tooltip_component.js";
import FileUploadComponent from "../src/javascripts/file_upload_component.js";
import ForumPostComponent from "../src/javascripts/forum_post_component.js";
import IqdbQuery from "../src/javascripts/iqdb_queries.js";
import Note from "../src/javascripts/notes.js";
import MediaAssetComponent from "../../components/media_asset_component/media_asset_component.js";
import PopupMenuComponent from "../../components/popup_menu_component/popup_menu_component.js";
import MediaAssetComponent from "../src/javascripts/media_asset_component.js";
import PopupMenuComponent from "../src/javascripts/popup_menu_component.js";
import Post from "../src/javascripts/posts.js";
import PostModeMenu from "../src/javascripts/post_mode_menu.js";
import PostTooltip from "../src/javascripts/post_tooltips.js";
import PostVotesTooltipComponent from "../../components/post_votes_tooltip_component/post_votes_tooltip_component.js";
import PostVotesTooltipComponent from "../src/javascripts/post_votes_tooltip_component.js";
import RelatedTag from "../src/javascripts/related_tag.js";
import Shortcuts from "../src/javascripts/shortcuts.js";
import TagCounter from "../src/javascripts/tag_counter.js";

View File

@@ -0,0 +1,52 @@
import Utility from "./utility";
class CommentComponent {
static initialize() {
if ($("#c-posts #a-show, #c-comments").length) {
$(document).on("click.danbooru.comment", ".edit_comment_link", CommentComponent.showEditForm);
$(document).on("click.danbooru.comment", ".expand-comment-response", CommentComponent.showNewCommentForm);
$(document).on("click.danbooru.comment", ".unhide-comment-link", CommentComponent.unhideComment);
$(document).on("click.danbooru.comment", ".comment-copy-id", CommentComponent.copyID);
$(document).on("click.danbooru.comment", ".comment-copy-link", CommentComponent.copyLink);
}
}
static showNewCommentForm(e) {
$(e.target).hide();
var $form = $(e.target).closest("div.new-comment").find("form");
$form.show();
$form[0].scrollIntoView(false);
$form.find("textarea").selectEnd();
e.preventDefault();
}
static showEditForm(e) {
$(this).closest(".comment").find(".edit_comment").show();
e.preventDefault();
}
static unhideComment(e) {
let $comment = $(this).closest(".comment");
$comment.find(".unhide-comment-link").hide();
$comment.find(".body").show();
e.preventDefault();
}
static async copyID(e) {
let id = $(this).closest(".comment").data("id");
let link = `comment #${id}`;
Utility.copyToClipboard(link);
e.preventDefault();
}
static async copyLink(e) {
let id = $(this).closest(".comment").data("id");
let link = `${window.location.origin}/comments/${id}`;
Utility.copyToClipboard(link);
e.preventDefault();
}
}
$(document).ready(CommentComponent.initialize);
export default CommentComponent;

View File

@@ -0,0 +1,65 @@
import Utility from "./utility";
import { delegate, hideAll } from 'tippy.js';
import 'tippy.js/dist/tippy.css';
class CommentVotesTooltipComponent {
// Trigger on the comment score link; see CommentComponent.
static TARGET_SELECTOR = "span.comment-score";
static SHOW_DELAY = 125;
static HIDE_DELAY = 125;
static DURATION = 250;
static instance = null;
static initialize() {
if ($(CommentVotesTooltipComponent.TARGET_SELECTOR).length === 0) {
return;
}
CommentVotesTooltipComponent.instance = delegate("body", {
allowHTML: true,
appendTo: document.querySelector("#comment-votes-tooltips"),
delay: [CommentVotesTooltipComponent.SHOW_DELAY, CommentVotesTooltipComponent.HIDE_DELAY],
duration: CommentVotesTooltipComponent.DURATION,
interactive: true,
maxWidth: "none",
target: CommentVotesTooltipComponent.TARGET_SELECTOR,
theme: "common-tooltip",
touch: false,
onShow: CommentVotesTooltipComponent.onShow,
onHide: CommentVotesTooltipComponent.onHide,
});
}
static async onShow(instance) {
let $target = $(instance.reference);
let $tooltip = $(instance.popper);
let commentId = $target.parents("[data-id]").data("id");
hideAll({ exclude: instance });
try {
$tooltip.addClass("tooltip-loading");
instance._request = $.get(`/comments/${commentId}/votes`, { variant: "tooltip" });
let html = await instance._request;
instance.setContent(html);
$tooltip.removeClass("tooltip-loading");
} catch (error) {
if (error.status !== 0 && error.statusText !== "abort") {
Utility.error(`Error displaying votes for comment #${commentId} (error: ${error.status} ${error.statusText})`);
}
}
}
static async onHide(instance) {
if (instance._request?.state() === "pending") {
instance._request.abort();
}
}
}
$(document).ready(CommentVotesTooltipComponent.initialize);
export default CommentVotesTooltipComponent;

View File

@@ -0,0 +1,65 @@
import Utility from "./utility";
import { delegate, hideAll } from 'tippy.js';
import 'tippy.js/dist/tippy.css';
class FavoritesTooltipComponent {
// Trigger on the post favcount link.
static TARGET_SELECTOR = "span.post-favcount a";
static SHOW_DELAY = 125;
static HIDE_DELAY = 125;
static DURATION = 250;
static instance = null;
static initialize() {
if ($(FavoritesTooltipComponent.TARGET_SELECTOR).length === 0) {
return;
}
FavoritesTooltipComponent.instance = delegate("body", {
allowHTML: true,
appendTo: document.querySelector("#post-favorites-tooltips"),
delay: [FavoritesTooltipComponent.SHOW_DELAY, FavoritesTooltipComponent.HIDE_DELAY],
duration: FavoritesTooltipComponent.DURATION,
interactive: true,
maxWidth: "none",
target: FavoritesTooltipComponent.TARGET_SELECTOR,
theme: "common-tooltip",
touch: false,
onShow: FavoritesTooltipComponent.onShow,
onHide: FavoritesTooltipComponent.onHide,
});
}
static async onShow(instance) {
let $target = $(instance.reference);
let $tooltip = $(instance.popper);
let postId = $target.parents("[data-id]").data("id");
hideAll({ exclude: instance });
try {
$tooltip.addClass("tooltip-loading");
instance._request = $.get(`/posts/${postId}/favorites?variant=tooltip`);
let html = await instance._request;
instance.setContent(html);
$tooltip.removeClass("tooltip-loading");
} catch (error) {
if (error.status !== 0 && error.statusText !== "abort") {
Utility.error(`Error displaying favorites for post #${postId} (error: ${error.status} ${error.statusText})`);
}
}
}
static async onHide(instance) {
if (instance._request?.state() === "pending") {
instance._request.abort();
}
}
}
$(document).ready(FavoritesTooltipComponent.initialize);
export default FavoritesTooltipComponent;

View File

@@ -0,0 +1,163 @@
import Dropzone from 'dropzone';
import Utility from "./utility";
import capitalize from "lodash/capitalize";
export default class FileUploadComponent {
static initialize() {
$(".file-upload-component").toArray().forEach(element => {
new FileUploadComponent($(element));
});
}
constructor($component) {
this.$component = $component;
this.$component.on("ajax:success", e => this.onSubmit(e));
this.$component.on("ajax:error", e => this.onError(e));
this.$dropTarget.on("paste.danbooru", e => this.onPaste(e));
this.dropzone = this.initializeDropzone();
// If the source field is pre-filled, then immediately submit the upload.
if (/^https?:\/\//.test(this.$sourceField.val())) {
this.$component.find("input[type='submit']").click();
}
}
initializeDropzone() {
if (!window.FileReader) {
this.$dropzone.addClass("hidden");
this.$component.find("input[type='file']").removeClass("hidden");
return null;
}
let dropzone = new Dropzone(this.$dropTarget.get(0), {
url: "/uploads.json",
paramName: "upload[file]",
clickable: this.$dropzone.get(0),
previewsContainer: this.$dropzone.get(0),
thumbnailHeight: null,
thumbnailWidth: null,
addRemoveLinks: false,
maxFiles: 1,
maxFilesize: this.maxFileSize,
maxThumbnailFilesize: this.maxFileSize,
timeout: 0,
acceptedFiles: "image/jpeg,image/png,image/gif,video/mp4,video/webm",
previewTemplate: this.$component.find(".dropzone-preview-template").html(),
});
dropzone.on("complete", file => {
this.$dropzone.find(".dz-progress").hide();
});
dropzone.on("addedfile", file => {
this.$dropzone.removeClass("error");
this.$dropzone.find(".dropzone-hint").hide();
// Remove all files except the file just added.
dropzone.files.forEach(f => {
if (f !== file) {
dropzone.removeFile(f);
}
});
});
dropzone.on("success", file => {
this.$dropzone.addClass("success");
let upload = JSON.parse(file.xhr.response)
this.pollStatus(upload);
});
dropzone.on("error", (file, msg) => {
this.$dropzone.addClass("error");
});
return dropzone;
}
onPaste(e) {
let url = e.originalEvent.clipboardData.getData("text");
this.$component.find("input[name='upload[source]']:not([disabled])").val(url);
if (/^https?:\/\//.test(url)) {
this.$component.find("input[type='submit']:not([disabled])").click();
}
e.preventDefault();
}
onSubmit(e) {
let upload = e.originalEvent.detail[0];
this.pollStatus(upload);
}
// Called after the upload is submitted via AJAX. Polls the upload until it
// is complete, then redirects to the upload page.
async pollStatus(upload) {
this.$component.find("progress").removeClass("hidden");
this.$component.find("input").attr("disabled", "disabled");
while (upload.status === "pending" || upload.status === "processing") {
await Utility.delay(500);
upload = await $.get(`/uploads/${upload.id}.json`);
}
if (upload.status === "completed") {
let params = new URLSearchParams(window.location.search);
let isBookmarklet = params.has("url");
params.delete("url");
params.delete("ref");
let url = new URL(`/uploads/${upload.id}`, window.location.origin);
url.search = params.toString();
if (isBookmarklet) {
window.location.replace(url);
} else {
window.location.assign(url);
}
} else if (upload.status === "error") {
this.$dropzone.removeClass("success");
this.$component.find("progress").addClass("hidden");
this.$component.find("input").removeAttr("disabled");
Utility.error(`Upload failed: ${upload.error}.`);
}
}
// Called when creating the upload failed because of a validation error (normally, because the source URL was not a real URL).
async onError(e) {
let errors = e.originalEvent.detail[0].errors;
let message = Object.keys(errors).map(attribute => {
return errors[attribute].map(error => {
if (attribute === "base") {
return `${error}`;
} else {
return `${capitalize(attribute)} ${error}`;
}
});
}).join("; ");
Utility.error(message);
}
get $dropzone() {
return this.$component.find(".dropzone-container");
}
get $sourceField() {
return this.$component.find("input[name='upload[source]']");
}
get maxFileSize() {
return Number(this.$component.attr("data-max-file-size")) / (1024 * 1024);
}
// The element to listen for drag and drop events and paste events. By default,
// it's the `.file-upload-component` element. If `data-drop-target` is the `body`
// element, then you can drop images or paste URLs anywhere on the page.
get $dropTarget() {
return $(this.$component.attr("data-drop-target") || this.$component);
}
}
$(FileUploadComponent.initialize);

View File

@@ -0,0 +1,48 @@
import Utility from "./utility";
class ForumPostComponent {
static initialize() {
if ($("#c-forum-topics #a-show, #c-forum-posts #a-show").length) {
$(document).on("click.danbooru.forum_post", ".edit_forum_post_link", ForumPostComponent.showEditPostForm);
$(document).on("click.danbooru.forum_post", ".edit_forum_topic_link", ForumPostComponent.showEditTopicForm);
$(document).on("click.danbooru.forum_post", "#new-response-link", ForumPostComponent.showNewForumPostForm);
$(document).on("click.danbooru.forum_post", ".forum-post-copy-id", ForumPostComponent.copyID);
$(document).on("click.danbooru.forum_post", ".forum-post-copy-link", ForumPostComponent.copyLink);
}
}
static showNewForumPostForm(e) {
$("#topic-response").show();
$("#forum_post_body").get(0).scrollIntoView(false);
$("#forum_post_body").selectEnd();
e.preventDefault();
}
static showEditPostForm(e) {
$(this).closest(".forum-post").find(".edit_forum_post").show();
e.preventDefault();
}
static showEditTopicForm(e) {
$(this).closest(".forum-post").find(".edit_forum_topic").show();
e.preventDefault();
}
static async copyID(e) {
let id = $(this).closest(".forum-post").data("id");
let link = `forum #${id}`;
Utility.copyToClipboard(link);
e.preventDefault();
}
static async copyLink(e) {
let id = $(this).closest(".forum-post").data("id");
let link = `${window.location.origin}/forum_posts/${id}`;
Utility.copyToClipboard(link);
e.preventDefault();
}
}
$(document).ready(ForumPostComponent.initialize);
export default ForumPostComponent;

View File

@@ -0,0 +1,56 @@
export default class MediaAssetComponent {
static initialize() {
$(".media-asset-component").toArray().forEach(element => {
new MediaAssetComponent(element);
});
}
constructor(element) {
this.$component = $(element);
if (this.$image.length) {
this.$image.on("click.danbooru", e => this.toggleFit());
new ResizeObserver(() => this.updateZoom()).observe(this.$image.get(0));
this.updateZoom();
}
}
toggleFit() {
this.$component.toggleClass("fit-screen");
this.updateZoom();
}
updateZoom() {
this.$image.removeClass("cursor-zoom-in cursor-zoom-out");
this.$zoomLevel.addClass("hidden").text(`${Math.round(100 * this.zoomLevel)}%`);
if (this.isDownscaled) {
this.$image.addClass("cursor-zoom-out");
this.$zoomLevel.removeClass("hidden");
} else if (this.isTooBig) {
this.$image.addClass("cursor-zoom-in");
}
}
get zoomLevel() {
return this.$image.width() / Number(this.$image.attr("width"));
}
get isDownscaled() {
return this.$image.width() < Number(this.$image.attr("width"));
}
get isTooBig() {
return this.$image.width() > this.$component.width();
}
get $image() {
return this.$component.find(".media-asset-image");
}
get $zoomLevel() {
return this.$component.find(".media-asset-zoom-level");
}
}
$(MediaAssetComponent.initialize);

View File

@@ -0,0 +1,36 @@
import { delegate } from 'tippy.js';
import 'tippy.js/dist/tippy.css';
class PopupMenuComponent {
static initialize() {
delegate("body", {
allowHTML: true,
interactive: true,
theme: "common-tooltip",
target: "a.popup-menu-button",
placement: "bottom-start",
trigger: "click",
touch: "hold",
animation: null,
content: PopupMenuComponent.content,
});
$(document).on("click.danbooru", ".popup-menu-content", PopupMenuComponent.onMenuItemClicked);
}
static content(element) {
let $content = $(element).parents(".popup-menu").find(".popup-menu-content");
$content.show();
return $content.get(0);
}
// Hides the menu when a menu item is clicked.
static onMenuItemClicked(event) {
let tippy = $(event.target).parents("[data-tippy-root]").get(0)._tippy;
tippy.hide();
}
}
$(document).ready(PopupMenuComponent.initialize);
export default PopupMenuComponent;

View File

@@ -0,0 +1,65 @@
import Utility from "./utility";
import { delegate, hideAll } from 'tippy.js';
import 'tippy.js/dist/tippy.css';
class PostVotesTooltipComponent {
// Trigger on the post score link; see PostVotesComponent.
static TARGET_SELECTOR = "span.post-votes span.post-score a";
static SHOW_DELAY = 125;
static HIDE_DELAY = 125;
static DURATION = 250;
static instance = null;
static initialize() {
if ($(PostVotesTooltipComponent.TARGET_SELECTOR).length === 0) {
return;
}
PostVotesTooltipComponent.instance = delegate("body", {
allowHTML: true,
appendTo: document.querySelector("#post-votes-tooltips"),
delay: [PostVotesTooltipComponent.SHOW_DELAY, PostVotesTooltipComponent.HIDE_DELAY],
duration: PostVotesTooltipComponent.DURATION,
interactive: true,
maxWidth: "none",
target: PostVotesTooltipComponent.TARGET_SELECTOR,
theme: "common-tooltip",
touch: false,
onShow: PostVotesTooltipComponent.onShow,
onHide: PostVotesTooltipComponent.onHide,
});
}
static async onShow(instance) {
let $target = $(instance.reference);
let $tooltip = $(instance.popper);
let postId = $target.parents("[data-id]").data("id");
hideAll({ exclude: instance });
try {
$tooltip.addClass("tooltip-loading");
instance._request = $.get(`/post_votes?search[post_id]=${postId}`, { variant: "tooltip" });
let html = await instance._request;
instance.setContent(html);
$tooltip.removeClass("tooltip-loading");
} catch (error) {
if (error.status !== 0 && error.statusText !== "abort") {
Utility.error(`Error displaying votes for post #${postId} (error: ${error.status} ${error.statusText})`);
}
}
}
static async onHide(instance) {
if (instance._request?.state() === "pending") {
instance._request.abort();
}
}
}
$(document).ready(PostVotesTooltipComponent.initialize);
export default PostVotesTooltipComponent;

View File

@@ -1,4 +1,4 @@
import SourceDataComponent from '../../../components/source_data_component/source_data_component.js';
import SourceDataComponent from "./source_data_component.js";
import Utility from './utility';
let RelatedTag = {};

View File

@@ -0,0 +1,22 @@
class SourceDataComponent {
static initialize() {
$(document).on("click.danbooru", ".source-data-fetch", SourceDataComponent.fetchData);
}
static async fetchData(e) {
let url = $("#post_source").val();
let ref = $("#post_referer_url").val();
e.preventDefault();
if (/^https?:\/\//.test(url)) {
$(".source-data").addClass("loading");
await $.get("/source.js", { url: url, ref: ref });
$(".source-data").removeClass("loading");
}
}
}
$(document).ready(SourceDataComponent.initialize);
export default SourceDataComponent;