264 lines
8.4 KiB
JavaScript
264 lines
8.4 KiB
JavaScript
(function () {
|
|
var MIN_WIDTH = 80;
|
|
var MAX_WIDTH = 560;
|
|
|
|
function csrfToken() {
|
|
var meta = document.querySelector("meta[name='csrf-token']");
|
|
return meta ? meta.getAttribute("content") : "";
|
|
}
|
|
|
|
function initEditor(root) {
|
|
if (root.dataset.ready === "1") return;
|
|
root.dataset.ready = "1";
|
|
|
|
var surface = root.querySelector(".invite-editor__surface");
|
|
var handle = root.querySelector(".invite-editor__handle");
|
|
var fileInput = root.querySelector("[data-invite-file]");
|
|
var htmlField = root.closest("form").querySelector("[data-invite-html]");
|
|
var noteInput = root.closest("form").querySelector("[data-invite-note]");
|
|
var status = root.querySelector("[data-invite-status]");
|
|
var selectedImg = null;
|
|
var drag = null;
|
|
|
|
function showStatus(message, isError) {
|
|
if (!status) return;
|
|
if (!message) {
|
|
status.hidden = true;
|
|
status.textContent = "";
|
|
return;
|
|
}
|
|
status.hidden = false;
|
|
status.textContent = message;
|
|
status.classList.toggle("invite-editor__status--error", !!isError);
|
|
}
|
|
|
|
function syncHtml() {
|
|
if (htmlField) htmlField.value = surface.innerHTML;
|
|
}
|
|
|
|
function syncNote() {
|
|
var note = (noteInput && noteInput.value || "").trim();
|
|
var el = surface.querySelector("[data-invite-note]");
|
|
if (!note) {
|
|
if (el) el.remove();
|
|
syncHtml();
|
|
return;
|
|
}
|
|
if (!el) {
|
|
el = document.createElement("p");
|
|
el.setAttribute("data-invite-note", "");
|
|
var first = surface.querySelector("p");
|
|
if (first && first.nextSibling) surface.insertBefore(el, first.nextSibling);
|
|
else if (first) first.after(el);
|
|
else surface.appendChild(el);
|
|
}
|
|
el.textContent = note;
|
|
syncHtml();
|
|
}
|
|
|
|
function prepareImage(img) {
|
|
img.draggable = false;
|
|
img.style.maxWidth = "100%";
|
|
img.style.height = "auto";
|
|
if (!img.getAttribute("width") && img.naturalWidth) {
|
|
img.setAttribute("width", Math.min(img.naturalWidth, MAX_WIDTH));
|
|
}
|
|
}
|
|
|
|
function updateHandle() {
|
|
if (!selectedImg || !handle) return;
|
|
var frame = handle.parentElement.getBoundingClientRect();
|
|
var rect = selectedImg.getBoundingClientRect();
|
|
handle.hidden = false;
|
|
handle.style.left = rect.right - frame.left - 8 + "px";
|
|
handle.style.top = rect.bottom - frame.top - 8 + "px";
|
|
}
|
|
|
|
function clearSelection() {
|
|
selectedImg = null;
|
|
if (handle) handle.hidden = true;
|
|
surface.querySelectorAll("img.invite-editor__img--selected").forEach(function (img) {
|
|
img.classList.remove("invite-editor__img--selected");
|
|
});
|
|
}
|
|
|
|
function selectImage(img) {
|
|
clearSelection();
|
|
selectedImg = img;
|
|
img.classList.add("invite-editor__img--selected");
|
|
updateHandle();
|
|
}
|
|
|
|
function insertImage(url) {
|
|
surface.focus();
|
|
document.execCommand("insertHTML", false, '<img src="' + url.replace(/"/g, """) + '" alt="">');
|
|
var images = surface.querySelectorAll("img");
|
|
var img = images[images.length - 1];
|
|
if (img) {
|
|
prepareImage(img);
|
|
selectImage(img);
|
|
}
|
|
syncHtml();
|
|
}
|
|
|
|
function uploadFile(file) {
|
|
if (!file || !file.type.match(/^image\/(png|jpeg|webp|gif)$/)) {
|
|
showStatus(root.getAttribute("data-msg-type"), true);
|
|
return;
|
|
}
|
|
if (file.size > 2 * 1024 * 1024) {
|
|
showStatus(root.getAttribute("data-msg-size"), true);
|
|
return;
|
|
}
|
|
var data = new FormData();
|
|
data.append("file", file);
|
|
showStatus(root.getAttribute("data-msg-uploading"), false);
|
|
fetch(root.getAttribute("data-upload-url"), {
|
|
method: "POST",
|
|
headers: { "X-CSRF-Token": csrfToken(), Accept: "application/json" },
|
|
body: data,
|
|
credentials: "same-origin"
|
|
}).then(function (res) {
|
|
return res.json().then(function (json) {
|
|
if (!res.ok) throw new Error(json.error || root.getAttribute("data-msg-failed"));
|
|
return json;
|
|
});
|
|
}).then(function (json) {
|
|
showStatus("", false);
|
|
insertImage(json.url);
|
|
}).catch(function (err) {
|
|
showStatus(err.message || root.getAttribute("data-msg-failed"), true);
|
|
});
|
|
}
|
|
|
|
root.querySelectorAll("[data-invite-cmd]").forEach(function (btn) {
|
|
btn.addEventListener("mousedown", function (e) {
|
|
e.preventDefault();
|
|
});
|
|
btn.addEventListener("click", function (e) {
|
|
e.preventDefault();
|
|
var cmd = btn.getAttribute("data-invite-cmd");
|
|
surface.focus();
|
|
if (cmd === "createLink") {
|
|
var url = window.prompt(root.getAttribute("data-msg-link") || "URL", "https://");
|
|
if (url) document.execCommand("createLink", false, url);
|
|
} else if (cmd === "image") {
|
|
fileInput.click();
|
|
} else {
|
|
document.execCommand(cmd, false, null);
|
|
}
|
|
syncHtml();
|
|
});
|
|
});
|
|
|
|
fileInput.addEventListener("change", function () {
|
|
var file = fileInput.files && fileInput.files[0];
|
|
fileInput.value = "";
|
|
if (file) uploadFile(file);
|
|
});
|
|
|
|
surface.addEventListener("click", function (e) {
|
|
if (e.target.tagName === "IMG") {
|
|
e.preventDefault();
|
|
selectImage(e.target);
|
|
} else {
|
|
clearSelection();
|
|
}
|
|
});
|
|
|
|
surface.addEventListener("input", syncHtml);
|
|
surface.addEventListener("blur", syncHtml);
|
|
if (noteInput) noteInput.addEventListener("input", syncNote);
|
|
|
|
surface.addEventListener("paste", function (e) {
|
|
var items = e.clipboardData && e.clipboardData.files;
|
|
if (items && items.length && items[0].type.indexOf("image/") === 0) {
|
|
e.preventDefault();
|
|
uploadFile(items[0]);
|
|
}
|
|
});
|
|
|
|
["dragenter", "dragover"].forEach(function (type) {
|
|
surface.addEventListener(type, function (e) {
|
|
e.preventDefault();
|
|
surface.classList.add("invite-editor__surface--drop");
|
|
});
|
|
});
|
|
["dragleave", "drop"].forEach(function (type) {
|
|
surface.addEventListener(type, function () {
|
|
surface.classList.remove("invite-editor__surface--drop");
|
|
});
|
|
});
|
|
surface.addEventListener("drop", function (e) {
|
|
e.preventDefault();
|
|
var file = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0];
|
|
if (file) uploadFile(file);
|
|
});
|
|
|
|
handle.addEventListener("mousedown", function (e) {
|
|
if (!selectedImg) return;
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
drag = {
|
|
startX: e.clientX,
|
|
startWidth: selectedImg.getBoundingClientRect().width
|
|
};
|
|
document.body.classList.add("invite-editor--resizing");
|
|
});
|
|
|
|
document.addEventListener("mousemove", function (e) {
|
|
if (!drag || !selectedImg) return;
|
|
var width = Math.round(Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, drag.startWidth + (e.clientX - drag.startX))));
|
|
selectedImg.style.width = width + "px";
|
|
selectedImg.style.height = "auto";
|
|
selectedImg.setAttribute("width", String(width));
|
|
selectedImg.removeAttribute("height");
|
|
updateHandle();
|
|
});
|
|
|
|
document.addEventListener("mouseup", function () {
|
|
if (!drag) return;
|
|
drag = null;
|
|
document.body.classList.remove("invite-editor--resizing");
|
|
syncHtml();
|
|
});
|
|
|
|
window.addEventListener("resize", updateHandle);
|
|
surface.querySelectorAll("img").forEach(prepareImage);
|
|
|
|
var form = root.closest("form");
|
|
form.addEventListener("submit", function () {
|
|
syncNote();
|
|
syncHtml();
|
|
});
|
|
form.addEventListener("turbo:submit-start", function () {
|
|
syncNote();
|
|
syncHtml();
|
|
});
|
|
syncNote();
|
|
syncHtml();
|
|
}
|
|
|
|
function bindSelectUncovered() {
|
|
document.querySelectorAll("[data-select-uncovered]").forEach(function (btn) {
|
|
if (btn.dataset.boundSelect === "1") return;
|
|
btn.dataset.boundSelect = "1";
|
|
btn.addEventListener("click", function (e) {
|
|
e.preventDefault();
|
|
var formId = btn.getAttribute("data-select-uncovered");
|
|
document.querySelectorAll('input[data-uncovered-match][form="' + formId + '"]').forEach(function (cb) {
|
|
cb.checked = true;
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
function boot() {
|
|
document.querySelectorAll("[data-invite-editor]").forEach(initEditor);
|
|
bindSelectUncovered();
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", boot);
|
|
document.addEventListener("turbo:load", boot);
|
|
})();
|