aboutsummaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
authorArno Richter <oelna@oelna.de>2022-12-21 15:05:28 +0100
committerArno Richter <oelna@oelna.de>2022-12-21 15:05:28 +0100
commit482fd7adee5e9e0990bf5904ed7d754d315de649 (patch)
tree7523a6f3482b6cb024624310f21fa7eeb05e9866 /js
parent057cace8b32e6c3d105695b517eae262071601f4 (diff)
downloadmicroblog-482fd7adee5e9e0990bf5904ed7d754d315de649.tar.gz
microblog-482fd7adee5e9e0990bf5904ed7d754d315de649.tar.bz2
microblog-482fd7adee5e9e0990bf5904ed7d754d315de649.zip
first attempt at image attachments!
Diffstat (limited to 'js')
-rw-r--r--js/microblog.js193
-rw-r--r--js/squircle.js146
2 files changed, 339 insertions, 0 deletions
diff --git a/js/microblog.js b/js/microblog.js
new file mode 100644
index 0000000..f7963e3
--- /dev/null
+++ b/js/microblog.js
@@ -0,0 +1,193 @@
+'use strict';
+
+document.documentElement.classList.remove('no-js');
+
+const textarea = document.querySelector('textarea[name="content"]');
+const charCount = document.querySelector('#count');
+
+if (textarea) {
+ const maxCount = parseInt(textarea.getAttribute('maxlength'));
+
+ if (textarea.value.length > 0) {
+ const textLength = [...textarea.value].length;
+ charCount.textContent = maxCount - textLength;
+ } else {
+ charCount.textContent = maxCount;
+ }
+
+ textarea.addEventListener('input', function () {
+ const textLength = [...this.value].length;
+
+ charCount.textContent = maxCount - textLength;
+ }, false);
+}
+
+const postForm = document.querySelector('#post-new-form');
+let useDragDrop = (!!window.FileReader && 'draggable' in document.createElement('div'));
+useDragDrop = true;
+if (postForm) {
+ const droparea = postForm.querySelector('#post-droparea');
+ const attachmentsInput = postForm.querySelector('#post-attachments');
+ const data = {
+ 'attachments': []
+ };
+
+
+ if (droparea && attachmentsInput) {
+ if (useDragDrop) {
+ console.log('init with modern file attachments');
+
+ const list = postForm.querySelector('#post-attachments-list');
+ list.addEventListener('click', function (e) {
+ e.preventDefault();
+
+ // remove attachment
+ if (e.target.nodeName.toLowerCase() == 'li') {
+ const filename = e.target.textContent;
+
+ data.attachments = data.attachments.filter(function (ele) {
+ return ele.name !== filename;
+ });
+
+ e.target.remove();
+ }
+ });
+
+ droparea.classList.remove('hidden');
+ document.querySelector('#post-attachments-label').classList.add('hidden');
+
+ droparea.ondragover = droparea.ondragenter = function (e) {
+ e.stopPropagation();
+ e.preventDefault();
+
+ e.dataTransfer.dropEffect = 'copy';
+ e.target.classList.add('drag');
+ };
+
+ droparea.ondragleave = function (e) {
+ e.target.classList.remove('drag');
+ };
+
+ droparea.onclick = function (e) {
+ e.preventDefault();
+
+ // make a virtual file upload
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.setAttribute('multiple', '');
+ input.setAttribute('accept', 'image/*'); // only images for now
+
+ input.onchange = e => {
+ processSelectedFiles(e.target.files);
+ }
+
+ input.click();
+ };
+
+ function processSelectedFiles(files) {
+ if (!files || files.length < 1) return;
+
+ for (const file of files) {
+ const found = data.attachments.find(ele => ele.name === file.name);
+ if(found) continue; // skip existing attachments
+
+ data.attachments.push({
+ 'name': file.name,
+ // todo: maybe some better form of dupe detection here?
+ 'file': file
+ });
+
+ const li = document.createElement('li');
+ li.textContent = file.name;
+
+ const reader = new FileReader();
+
+ if(file.type.startsWith('image/')) {
+ reader.onload = function (e) {
+ var dataURL = e.target.result;
+
+ const preview = document.createElement('img');
+ preview.classList.add('file-preview');
+ preview.setAttribute('src', dataURL);
+
+ li.prepend(preview);
+ };
+ reader.onerror = function (e) {
+ console.log('An error occurred during file input: '+e.target.error.code);
+ };
+
+ reader.readAsDataURL(file);
+ }
+
+ list.append(li);
+ }
+ }
+
+ droparea.ondrop = function (e) {
+ if (e.dataTransfer) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ processSelectedFiles(e.dataTransfer.files);
+ }
+
+ e.target.classList.remove('drag');
+ };
+
+ postForm.addEventListener('submit', async function (e) {
+ e.preventDefault();
+
+ const postFormData = new FormData();
+
+ postFormData.append('content', postForm.querySelector('[name="content"]').value);
+
+ for (const attachment of data.attachments) {
+ postFormData.append('attachments[]', attachment.file);
+ }
+
+ /*
+ for (const pair of postFormData.entries()) {
+ console.log(`${pair[0]}, ${pair[1]}`);
+ }
+ */
+
+ const response = await fetch(postForm.getAttribute('action'), {
+ body: postFormData,
+ method: 'POST'
+ });
+
+ if (response.ok && response.status == 200) {
+ const txt = await response.text();
+ // console.log(response, txt);
+ window.location.href = postForm.dataset.redirect;
+ } else {
+ console.warn('error during post submission!', response);
+ }
+ });
+ } else {
+ // use the native file input dialog
+ // but enhanced
+ if (attachmentsInput) {
+ console.log('init with classic file attachments');
+
+ attachmentsInput.addEventListener('change', function (e) {
+ console.log(e.target.files);
+
+ const list = postForm.querySelector('#post-attachments-list');
+ list.replaceChildren();
+
+ for (const file of e.target.files) {
+ const li = document.createElement('li');
+ li.textContent = file.name;
+ list.append(li);
+ }
+ });
+ }
+ }
+ }
+}
+
+// better rounded corners
+if ('paintWorklet' in CSS) {
+ // CSS.paintWorklet.addModule('./js/squircle.js');
+}
diff --git a/js/squircle.js b/js/squircle.js
new file mode 100644
index 0000000..063a383
--- /dev/null
+++ b/js/squircle.js
@@ -0,0 +1,146 @@
+const drawSquircle = (ctx, geom, radius, smooth, lineWidth, color) => {
+ const defaultFill = color;
+ const lineWidthOffset = lineWidth / 2;
+ // OPEN LEFT-TOP CORNER
+ ctx.beginPath();
+ ctx.lineTo(radius, lineWidthOffset);
+ // TOP-RIGHT CORNER
+ ctx.lineTo(geom.width - radius, lineWidthOffset);
+ ctx.bezierCurveTo(
+ geom.width - radius / smooth,
+ lineWidthOffset, // first bezier point
+ geom.width - lineWidthOffset,
+ radius / smooth, // second bezier point
+ geom.width - lineWidthOffset,
+ radius // last connect point
+ );
+ // BOTTOM-RIGHT CORNER
+ ctx.lineTo(geom.width - lineWidthOffset, geom.height - radius);
+ ctx.bezierCurveTo(
+ geom.width - lineWidthOffset,
+ geom.height - radius / smooth, // first bezier point
+ geom.width - radius / smooth,
+ geom.height - lineWidthOffset, // second bezier point
+ geom.width - radius,
+ geom.height - lineWidthOffset // last connect point
+ );
+ // BOTTOM-LEFT CORNER
+ ctx.lineTo(radius, geom.height - lineWidthOffset);
+ ctx.bezierCurveTo(
+ radius / smooth,
+ geom.height - lineWidthOffset, // first bezier point
+ lineWidthOffset,
+ geom.height - radius / smooth, // second bezier point
+ lineWidthOffset,
+ geom.height - radius // last connect point
+ );
+ // CLOSE LEFT-TOP CORNER
+ ctx.lineTo(lineWidthOffset, radius);
+ ctx.bezierCurveTo(
+ lineWidthOffset,
+ radius / smooth, // first bezier point
+ radius / smooth,
+ lineWidthOffset, // second bezier point
+ radius,
+ lineWidthOffset // last connect point
+ );
+ ctx.closePath();
+
+ if (lineWidth) {
+ // console.log(lineWidth);
+ ctx.strokeStyle = defaultFill;
+ ctx.lineWidth = lineWidth;
+ ctx.stroke();
+ } else {
+ ctx.fillStyle = defaultFill;
+ ctx.fill();
+ }
+};
+
+if (typeof registerPaint !== "undefined") {
+ class SquircleClass {
+ static get contextOptions() {
+ return { alpha: true };
+ }
+ static get inputProperties() {
+ return [
+ "--squircle-radius",
+ "--squircle-smooth",
+ "--squircle-outline",
+ "--squircle-fill",
+ "--squircle-ratio",
+ ];
+ }
+
+ paint(ctx, geom, properties) {
+ const customRatio = properties.get("--squircle-ratio");
+ const smoothRatio = 10;
+ const distanceRatio = parseFloat(customRatio)
+ ? parseFloat(customRatio)
+ : 1.8;
+ const squircleSmooth = parseFloat(
+ properties.get("--squircle-smooth") * smoothRatio
+ );
+ const squircleRadius =
+ parseInt(properties.get("--squircle-radius"), 10) * distanceRatio;
+ const squrcleOutline = parseFloat(
+ properties.get("--squircle-outline"),
+ 10
+ );
+ const squrcleColor = properties
+ .get("--squircle-fill")
+ .toString()
+ .replace(/\s/g, "");
+
+ const isSmooth = () => {
+ if (typeof properties.get("--squircle-smooth")[0] !== "undefined") {
+ if (squircleSmooth === 0) {
+ return 1;
+ }
+ return squircleSmooth;
+ } else {
+ return 10;
+ }
+ };
+
+ const isOutline = () => {
+ if (squrcleOutline) {
+ return squrcleOutline;
+ } else {
+ return 0;
+ }
+ };
+
+ const isColor = () => {
+ if (squrcleColor) {
+ return squrcleColor;
+ } else {
+ return "#f45";
+ }
+ };
+
+ if (squircleRadius < geom.width / 2 && squircleRadius < geom.height / 2) {
+ drawSquircle(
+ ctx,
+ geom,
+ squircleRadius,
+ isSmooth(),
+ isOutline(),
+ isColor()
+ );
+ } else {
+ drawSquircle(
+ ctx,
+ geom,
+ Math.min(geom.width / 2, geom.height / 2),
+ isSmooth(),
+ isOutline(),
+ isColor()
+ );
+ }
+ }
+ }
+
+ // eslint-disable-next-line no-undef
+ registerPaint("squircle", SquircleClass);
+} \ No newline at end of file