457 lines
13 KiB
JavaScript
457 lines
13 KiB
JavaScript
"use strict";
|
|
|
|
const events = require("../events.js");
|
|
const api = require("../api.js");
|
|
const views = require("../util/views.js");
|
|
const FileDropperControl = require("../controls/file_dropper_control.js");
|
|
|
|
const template = views.getTemplate("post-upload");
|
|
const rowTemplate = views.getTemplate("post-upload-row");
|
|
|
|
function _mimeTypeToPostType(mimeType) {
|
|
return (
|
|
{
|
|
"application/x-shockwave-flash": "flash",
|
|
"image/gif": "image",
|
|
"image/jpeg": "image",
|
|
"image/png": "image",
|
|
"image/webp": "image",
|
|
"image/bmp": "image",
|
|
"image/avif": "image",
|
|
"image/heif": "image",
|
|
"image/heic": "image",
|
|
"video/mp4": "video",
|
|
"video/webm": "video",
|
|
"video/quicktime": "video",
|
|
}[mimeType] || "unknown"
|
|
);
|
|
}
|
|
|
|
class Uploadable extends events.EventTarget {
|
|
constructor() {
|
|
super();
|
|
this.lookalikes = [];
|
|
this.lookalikesConfirmed = false;
|
|
this.safety = "safe";
|
|
this.flags = [];
|
|
this.tags = [];
|
|
this.relations = [];
|
|
this.anonymous = !api.isLoggedIn();
|
|
this.forceAnonymous = !api.isLoggedIn();
|
|
}
|
|
|
|
destroy() {}
|
|
|
|
get mimeType() {
|
|
return "application/octet-stream";
|
|
}
|
|
|
|
get type() {
|
|
return _mimeTypeToPostType(this.mimeType);
|
|
}
|
|
|
|
get key() {
|
|
throw new Error("Not implemented");
|
|
}
|
|
|
|
get name() {
|
|
throw new Error("Not implemented");
|
|
}
|
|
}
|
|
|
|
class File extends Uploadable {
|
|
constructor(file) {
|
|
super();
|
|
this.file = file;
|
|
|
|
this._previewUrl = null;
|
|
if (URL && URL.createObjectURL) {
|
|
this._previewUrl = URL.createObjectURL(file);
|
|
} else {
|
|
let reader = new FileReader();
|
|
reader.readAsDataURL(file);
|
|
reader.addEventListener("load", (e) => {
|
|
this._previewUrl = e.target.result;
|
|
this.dispatchEvent(
|
|
new CustomEvent("finish", { detail: { uploadable: this } })
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
if (URL && URL.createObjectURL && URL.revokeObjectURL) {
|
|
URL.revokeObjectURL(this._previewUrl);
|
|
}
|
|
}
|
|
|
|
get mimeType() {
|
|
return this.file.type;
|
|
}
|
|
|
|
get previewUrl() {
|
|
return this._previewUrl;
|
|
}
|
|
|
|
get key() {
|
|
return this.file.name + this.file.size;
|
|
}
|
|
|
|
get name() {
|
|
return this.file.name;
|
|
}
|
|
}
|
|
|
|
class Url extends Uploadable {
|
|
constructor(url) {
|
|
super();
|
|
this.url = url;
|
|
this.dispatchEvent(new CustomEvent("finish"));
|
|
}
|
|
|
|
get mimeType() {
|
|
let mime = {
|
|
swf: "application/x-shockwave-flash",
|
|
jpg: "image/jpeg",
|
|
png: "image/png",
|
|
gif: "image/gif",
|
|
webp: "image/webp",
|
|
bmp: "image/bmp",
|
|
avif: "image/avif",
|
|
heif: "image/heif",
|
|
heic: "image/heic",
|
|
mp4: "video/mp4",
|
|
mov: "video/quicktime",
|
|
webm: "video/webm",
|
|
};
|
|
for (let extension of Object.keys(mime)) {
|
|
if (this.url.toLowerCase().indexOf("." + extension) !== -1) {
|
|
return mime[extension];
|
|
}
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
get previewUrl() {
|
|
return this.url;
|
|
}
|
|
|
|
get key() {
|
|
return this.url;
|
|
}
|
|
|
|
get name() {
|
|
return this.url;
|
|
}
|
|
}
|
|
|
|
class PostUploadView extends events.EventTarget {
|
|
constructor(ctx) {
|
|
super();
|
|
this._ctx = ctx;
|
|
this._hostNode = document.getElementById("content-holder");
|
|
|
|
views.replaceContent(this._hostNode, template());
|
|
views.syncScrollPosition();
|
|
|
|
this._cancelButtonNode.disabled = true;
|
|
|
|
this._uploadables = [];
|
|
this._uploadables.find = (u) => {
|
|
return this._uploadables.findIndex((u2) => u.key === u2.key);
|
|
};
|
|
|
|
this._contentFileDropper = new FileDropperControl(
|
|
this._contentInputNode,
|
|
{
|
|
extraText:
|
|
"Allowed extensions: .jpg, .png, .gif, .webm, .mp4, .swf, .avif, .heif, .heic",
|
|
allowUrls: true,
|
|
allowMultiple: true,
|
|
lock: false,
|
|
}
|
|
);
|
|
this._contentFileDropper.addEventListener("fileadd", (e) =>
|
|
this._evtFilesAdded(e)
|
|
);
|
|
this._contentFileDropper.addEventListener("urladd", (e) =>
|
|
this._evtUrlsAdded(e)
|
|
);
|
|
|
|
this._cancelButtonNode.addEventListener("click", (e) =>
|
|
this._evtCancelButtonClick(e)
|
|
);
|
|
this._formNode.addEventListener("submit", (e) =>
|
|
this._evtFormSubmit(e)
|
|
);
|
|
this._formNode.classList.add("inactive");
|
|
}
|
|
|
|
enableForm() {
|
|
views.enableForm(this._formNode);
|
|
this._cancelButtonNode.disabled = true;
|
|
this._formNode.classList.remove("uploading");
|
|
}
|
|
|
|
disableForm() {
|
|
views.disableForm(this._formNode);
|
|
this._cancelButtonNode.disabled = false;
|
|
this._formNode.classList.add("uploading");
|
|
}
|
|
|
|
clearMessages() {
|
|
views.clearMessages(this._hostNode);
|
|
}
|
|
|
|
showSuccess(message) {
|
|
views.showSuccess(this._hostNode, message);
|
|
}
|
|
|
|
showError(message, uploadable) {
|
|
this._showMessage(views.showError, message, uploadable);
|
|
}
|
|
|
|
showInfo(message, uploadable) {
|
|
this._showMessage(views.showInfo, message, uploadable);
|
|
views.appendExclamationMark();
|
|
}
|
|
|
|
_showMessage(functor, message, uploadable) {
|
|
functor(uploadable ? uploadable.rowNode : this._hostNode, message);
|
|
}
|
|
|
|
addUploadables(uploadables) {
|
|
this._formNode.classList.remove("inactive");
|
|
let duplicatesFound = 0;
|
|
for (let uploadable of uploadables) {
|
|
if (this._uploadables.find(uploadable) !== -1) {
|
|
duplicatesFound++;
|
|
continue;
|
|
}
|
|
this._uploadables.push(uploadable);
|
|
this._emit("change");
|
|
this._renderRowNode(uploadable);
|
|
uploadable.addEventListener("finish", (e) =>
|
|
this._updateThumbnailNode(e.detail.uploadable)
|
|
);
|
|
}
|
|
if (duplicatesFound) {
|
|
let message = null;
|
|
if (duplicatesFound < uploadables.length) {
|
|
message =
|
|
"Some of the files were already added " +
|
|
"and have been skipped.";
|
|
} else if (duplicatesFound === 1) {
|
|
message = "This file was already added.";
|
|
} else {
|
|
message = "These files were already added.";
|
|
}
|
|
alert(message);
|
|
}
|
|
}
|
|
|
|
removeUploadable(uploadable) {
|
|
if (this._uploadables.find(uploadable) === -1) {
|
|
return;
|
|
}
|
|
uploadable.destroy();
|
|
uploadable.rowNode.parentNode.removeChild(uploadable.rowNode);
|
|
this._uploadables.splice(this._uploadables.find(uploadable), 1);
|
|
this._emit("change");
|
|
if (!this._uploadables.length) {
|
|
this._formNode.classList.add("inactive");
|
|
this._submitButtonNode.value = "Upload all";
|
|
}
|
|
}
|
|
|
|
updateUploadable(uploadable) {
|
|
uploadable.lookalikesConfirmed = true;
|
|
this._renderRowNode(uploadable);
|
|
}
|
|
|
|
_evtFilesAdded(e) {
|
|
this.addUploadables(e.detail.files.map((file) => new File(file)));
|
|
}
|
|
|
|
_evtUrlsAdded(e) {
|
|
this.addUploadables(e.detail.urls.map((url) => new Url(url)));
|
|
}
|
|
|
|
_evtCancelButtonClick(e) {
|
|
e.preventDefault();
|
|
this._emit("cancel");
|
|
}
|
|
|
|
_evtFormSubmit(e) {
|
|
e.preventDefault();
|
|
for (let uploadable of this._uploadables) {
|
|
this._updateUploadableFromDom(uploadable);
|
|
}
|
|
this._submitButtonNode.value = "Resume";
|
|
this._emit("submit");
|
|
}
|
|
|
|
_updateUploadableFromDom(uploadable) {
|
|
const rowNode = uploadable.rowNode;
|
|
|
|
const safetyNode = rowNode.querySelector(".safety input:checked");
|
|
if (safetyNode) {
|
|
uploadable.safety = safetyNode.value;
|
|
}
|
|
|
|
const anonymousNode = rowNode.querySelector(
|
|
".anonymous input:checked"
|
|
);
|
|
if (anonymousNode) {
|
|
uploadable.anonymous = true;
|
|
}
|
|
|
|
uploadable.tags = [];
|
|
uploadable.relations = [];
|
|
for (let [i, lookalike] of uploadable.lookalikes.entries()) {
|
|
let lookalikeNode = rowNode.querySelector(
|
|
`.lookalikes li:nth-child(${i + 1})`
|
|
);
|
|
if (lookalikeNode.querySelector("[name=copy-tags]").checked) {
|
|
uploadable.tags = uploadable.tags.concat(
|
|
lookalike.post.tagNames
|
|
);
|
|
}
|
|
if (lookalikeNode.querySelector("[name=add-relation]").checked) {
|
|
uploadable.relations.push(lookalike.post.id);
|
|
}
|
|
}
|
|
}
|
|
|
|
_evtRemoveClick(e, uploadable) {
|
|
e.preventDefault();
|
|
if (this._uploading) {
|
|
return;
|
|
}
|
|
this.removeUploadable(uploadable);
|
|
}
|
|
|
|
_evtMoveClick(e, uploadable, delta) {
|
|
e.preventDefault();
|
|
if (this._uploading) {
|
|
return;
|
|
}
|
|
let index = this._uploadables.find(uploadable);
|
|
if ((index + delta).between(-1, this._uploadables.length)) {
|
|
let uploadable1 = this._uploadables[index];
|
|
let uploadable2 = this._uploadables[index + delta];
|
|
this._uploadables[index] = uploadable2;
|
|
this._uploadables[index + delta] = uploadable1;
|
|
if (delta === 1) {
|
|
this._listNode.insertBefore(
|
|
uploadable2.rowNode,
|
|
uploadable1.rowNode
|
|
);
|
|
} else {
|
|
this._listNode.insertBefore(
|
|
uploadable1.rowNode,
|
|
uploadable2.rowNode
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
_emit(eventType) {
|
|
this.dispatchEvent(
|
|
new CustomEvent(eventType, {
|
|
detail: {
|
|
uploadables: this._uploadables,
|
|
skipDuplicates: this._skipDuplicatesCheckboxNode.checked,
|
|
alwaysUploadSimilar:
|
|
this._alwaysUploadSimilarCheckboxNode.checked,
|
|
pauseRemainOnError:
|
|
this._pauseRemainOnErrorCheckboxNode.checked,
|
|
},
|
|
})
|
|
);
|
|
}
|
|
|
|
_renderRowNode(uploadable) {
|
|
const rowNode = rowTemplate(
|
|
Object.assign({}, this._ctx, { uploadable: uploadable })
|
|
);
|
|
if (uploadable.rowNode) {
|
|
uploadable.rowNode.parentNode.replaceChild(
|
|
rowNode,
|
|
uploadable.rowNode
|
|
);
|
|
} else {
|
|
this._listNode.appendChild(rowNode);
|
|
}
|
|
|
|
uploadable.rowNode = rowNode;
|
|
|
|
rowNode
|
|
.querySelector("a.remove")
|
|
.addEventListener("click", (e) =>
|
|
this._evtRemoveClick(e, uploadable)
|
|
);
|
|
rowNode
|
|
.querySelector("a.move-up")
|
|
.addEventListener("click", (e) =>
|
|
this._evtMoveClick(e, uploadable, -1)
|
|
);
|
|
rowNode
|
|
.querySelector("a.move-down")
|
|
.addEventListener("click", (e) =>
|
|
this._evtMoveClick(e, uploadable, 1)
|
|
);
|
|
}
|
|
|
|
_updateThumbnailNode(uploadable) {
|
|
const rowNode = rowTemplate(
|
|
Object.assign({}, this._ctx, { uploadable: uploadable })
|
|
);
|
|
views.replaceContent(
|
|
uploadable.rowNode.querySelector(".thumbnail"),
|
|
rowNode.querySelector(".thumbnail").childNodes
|
|
);
|
|
}
|
|
|
|
get _uploading() {
|
|
return this._formNode.classList.contains("uploading");
|
|
}
|
|
|
|
get _listNode() {
|
|
return this._hostNode.querySelector(".uploadables-container");
|
|
}
|
|
|
|
get _formNode() {
|
|
return this._hostNode.querySelector("form");
|
|
}
|
|
|
|
get _skipDuplicatesCheckboxNode() {
|
|
return this._hostNode.querySelector("form [name=skip-duplicates]");
|
|
}
|
|
|
|
get _alwaysUploadSimilarCheckboxNode() {
|
|
return this._hostNode.querySelector(
|
|
"form [name=always-upload-similar]"
|
|
);
|
|
}
|
|
|
|
get _pauseRemainOnErrorCheckboxNode() {
|
|
return this._hostNode.querySelector(
|
|
"form [name=pause-remain-on-error]"
|
|
);
|
|
}
|
|
|
|
get _submitButtonNode() {
|
|
return this._hostNode.querySelector("form [type=submit]");
|
|
}
|
|
|
|
get _cancelButtonNode() {
|
|
return this._hostNode.querySelector("form .cancel");
|
|
}
|
|
|
|
get _contentInputNode() {
|
|
return this._formNode.querySelector(".dropper-container");
|
|
}
|
|
}
|
|
|
|
module.exports = PostUploadView;
|