150 lines
5.0 KiB
JavaScript
150 lines
5.0 KiB
JavaScript
(function () {
|
|
function setImageSize(image, size) {
|
|
image.classList.remove('blog-image-small', 'blog-image-medium', 'blog-image-full');
|
|
image.style.removeProperty('width');
|
|
image.style.removeProperty('height');
|
|
image.classList.add('blog-image-' + size);
|
|
}
|
|
|
|
function setImageWidth(image, width) {
|
|
var px = parseInt(width, 10);
|
|
if (!Number.isFinite(px) || px < 40) return;
|
|
image.classList.remove('blog-image-small', 'blog-image-medium', 'blog-image-full');
|
|
image.style.width = Math.min(px, 1200) + 'px';
|
|
image.style.height = 'auto';
|
|
}
|
|
|
|
function normalizeEditorImages(root) {
|
|
root.querySelectorAll('img').forEach(function (image) {
|
|
if (
|
|
!image.classList.contains('blog-image-small')
|
|
&& !image.classList.contains('blog-image-medium')
|
|
&& !image.classList.contains('blog-image-full')
|
|
) {
|
|
image.classList.add('blog-image-full');
|
|
}
|
|
});
|
|
}
|
|
|
|
function initEditor(form) {
|
|
var editorEl = form.querySelector('[data-rich-editor]');
|
|
var contentInput = form.querySelector('[data-rich-content]');
|
|
var status = form.querySelector('[data-rich-status]');
|
|
var imageControls = form.querySelector('[data-image-size-controls]');
|
|
var imageWidthInput = form.querySelector('[data-image-width]');
|
|
if (!editorEl || !contentInput || !window.Quill) return;
|
|
|
|
var selectedImage = null;
|
|
var toolbar = [
|
|
[{ header: [2, 3, false] }],
|
|
['bold', 'italic'],
|
|
[{ list: 'ordered' }, { list: 'bullet' }],
|
|
['link', 'image'],
|
|
['clean']
|
|
];
|
|
var editor = new Quill(editorEl, {
|
|
modules: { toolbar: toolbar },
|
|
placeholder: '',
|
|
theme: 'snow'
|
|
});
|
|
var initialContent = contentInput.value.trim();
|
|
if (initialContent) {
|
|
if (initialContent.indexOf('<') >= 0) editor.clipboard.dangerouslyPasteHTML(initialContent);
|
|
else editor.setText(initialContent);
|
|
normalizeEditorImages(editor.root);
|
|
}
|
|
|
|
function syncContent() {
|
|
normalizeEditorImages(editor.root);
|
|
contentInput.value = editor.root.innerHTML;
|
|
}
|
|
|
|
function setStatus(message) {
|
|
if (status) status.textContent = message || '';
|
|
}
|
|
|
|
function chooseImageFile() {
|
|
var input = document.createElement('input');
|
|
input.type = 'file';
|
|
input.accept = 'image/jpeg,image/png,image/webp,image/gif';
|
|
input.addEventListener('change', function () {
|
|
var file = input.files && input.files[0];
|
|
if (!file) return;
|
|
uploadImage(file);
|
|
});
|
|
input.click();
|
|
}
|
|
|
|
async function uploadImage(file) {
|
|
var formData = new FormData();
|
|
formData.append('file', file);
|
|
setStatus(status ? status.dataset.uploading : '');
|
|
try {
|
|
var response = await fetch('/images/upload', {
|
|
method: 'POST',
|
|
body: formData,
|
|
credentials: 'same-origin'
|
|
});
|
|
if (!response.ok) throw new Error('upload failed');
|
|
var result = await response.json();
|
|
var range = editor.getSelection(true);
|
|
editor.insertEmbed(range.index, 'image', result.url, 'user');
|
|
editor.setSelection(range.index + 1, 0, 'silent');
|
|
window.setTimeout(function () {
|
|
var images = editor.root.querySelectorAll('img');
|
|
var image = images[images.length - 1];
|
|
if (image) {
|
|
setImageSize(image, 'full');
|
|
selectedImage = image;
|
|
if (imageControls) imageControls.classList.remove('hidden');
|
|
}
|
|
syncContent();
|
|
}, 0);
|
|
setStatus(status ? status.dataset.uploaded : '');
|
|
} catch (_error) {
|
|
setStatus(status ? status.dataset.error : '');
|
|
}
|
|
}
|
|
|
|
editor.getModule('toolbar').addHandler('image', chooseImageFile);
|
|
|
|
editor.root.addEventListener('click', function (event) {
|
|
if (event.target && event.target.tagName === 'IMG') {
|
|
selectedImage = event.target;
|
|
if (imageWidthInput) imageWidthInput.value = parseInt(selectedImage.style.width, 10) || '';
|
|
if (imageControls) imageControls.classList.remove('hidden');
|
|
}
|
|
});
|
|
|
|
if (imageControls) {
|
|
imageControls.addEventListener('click', function (event) {
|
|
var button = event.target.closest('[data-image-size]');
|
|
if (button && selectedImage) {
|
|
setImageSize(selectedImage, button.dataset.imageSize);
|
|
if (imageWidthInput) imageWidthInput.value = '';
|
|
syncContent();
|
|
}
|
|
});
|
|
}
|
|
|
|
if (imageWidthInput) {
|
|
imageWidthInput.addEventListener('change', function () {
|
|
if (!selectedImage) return;
|
|
setImageWidth(selectedImage, imageWidthInput.value);
|
|
syncContent();
|
|
});
|
|
}
|
|
|
|
editor.on('text-change', syncContent);
|
|
form.addEventListener('submit', syncContent);
|
|
syncContent();
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
document.querySelectorAll('[data-rich-editor]').forEach(function (editorEl) {
|
|
var form = editorEl.closest('form');
|
|
if (form) initEditor(form);
|
|
});
|
|
});
|
|
})();
|