itty-bitty/docs/edit.js

445 lines
12 KiB
JavaScript
Raw Normal View History

2022-02-15 23:28:50 +00:00
import * as bitty from './bitty.js';
2022-06-28 16:04:53 +00:00
window.bitty = bitty;
2018-07-08 16:18:51 +00:00
var QS = document.querySelector.bind(document);
var QSS = document.querySelectorAll.bind(document);
2018-05-26 00:16:21 +00:00
2018-07-08 16:18:51 +00:00
var DATA_PREFIX = "data:text/html;base64,";
var DATA_PREFIX_8 = "data:text/html;charset=utf-8;base64,";
var DATA_PREFIX_BXZE = "data:text/html;charset=utf-8;bxze64,";
2022-05-29 05:34:44 +00:00
var DATA_PREFIX_GZIP = "data:text/html;charset=utf-8;gzip64,";
2018-06-10 14:41:58 +00:00
2018-07-08 16:18:51 +00:00
var b = document.documentElement.setAttribute(
"data-useragent",
navigator.userAgent
);
2018-05-26 00:16:21 +00:00
2022-06-28 16:04:53 +00:00
var bindings = {}
var quill = new Quill('#editor', {
theme: 'snow',
modules: {
syntax: true,
keyboard: { bindings },
2022-06-29 15:34:40 +00:00
toolbar: "#formatbar"
2022-06-28 16:04:53 +00:00
}
});
quill.on('text-change', function(delta, oldDelta, source) {
if (source == 'api') {
console.debug("An API call triggered this change.");
} else if (source == 'user') {
console.debug("A user action triggered this change.", source);
}
handleContentChange();
});
quill.setSelection(0, Infinity);
var editor = quill.root;
editor.autocomplete="off";
2018-07-04 19:22:21 +00:00
var importedFileData = undefined;
2022-06-28 16:04:53 +00:00
var content = editor;
2022-06-30 15:49:56 +00:00
window.onload = async function() {
2018-07-08 16:18:51 +00:00
window.onpopstate = function(e) {
setContent(e.state);
};
window.onhashchange = function(e) {
console.log("hash", e);
location.reload();
};
document.body.onclick = function(e) {
2022-06-28 16:04:53 +00:00
if (e.target == document.body) editor.focus();
2018-07-08 16:18:51 +00:00
};
2022-06-29 01:54:46 +00:00
content = document.getElementById("content");
let lastTarget;
window.addEventListener("dragenter", function(e){ // drag start
console.log("enter", e.target)
document.body.classList.toggle("dragging", true);
lastTarget = e.target; // cache the last target here
});
window.addEventListener("dragleave", function (e) { // user canceled
console.log("exit ", e.target)
if(e.target === lastTarget || e.target === document) {
document.body.classList.toggle("dragging", false);
}
});
2022-06-28 16:04:53 +00:00
2022-06-30 14:24:44 +00:00
if (localStorage.getItem("preview")) {
togglePreview(true)
}
2022-06-29 15:34:40 +00:00
// content.addEventListener("keydown", handleKey);
// content.addEventListener("keyup", handleInput);
2018-07-08 16:18:51 +00:00
QS("#doc-title").addEventListener("keyup", handleInput);
2022-06-28 16:04:53 +00:00
Array.from(document.getElementsByTagName("input")).forEach(i => i.addEventListener("keydown", handleInput))
2022-06-29 01:54:46 +00:00
document.getElementById("drop-zone").addEventListener("drop", handleDrop);
document.getElementById("drop-zone").addEventListener("dragover", e => e.preventDefault());
2022-06-29 15:34:40 +00:00
editor.addEventListener("paste", handlePaste);
2022-06-28 16:04:53 +00:00
// content.contentEditable = "true";
editor.focus();
// document.execCommand("selectAll", false, null);
2018-07-08 16:18:51 +00:00
QS("#qrcode").onclick = makeQRCode;
2022-06-28 16:04:53 +00:00
QS("#upload").onclick = upload;
2022-05-17 15:37:07 +00:00
QS("#share").onclick = share;
if (!navigator.share) QS("#share").style.display = "none"
2018-07-08 16:18:51 +00:00
QS("#twitter").onclick = tweetLink;
QS("#copy").onclick = copyLink;
2022-06-29 15:34:40 +00:00
QS("#preview").onclick = togglePreview;
2022-06-30 15:49:56 +00:00
QS("#format-toggle").onclick = toggleFormat;
2022-06-28 16:04:53 +00:00
QS("#mainmenu").onclick = () => { toggleMenu(QS("#mainmenu"))};
QS("#doc-title").onclick = toggleMetadata;
2022-06-29 15:34:40 +00:00
QS("#doc-title").onclick = toggleMetadata;
2018-07-08 16:18:51 +00:00
var hash = window.location.hash.substring(1);
2018-05-26 00:16:21 +00:00
if (hash.length) {
2018-06-10 14:41:58 +00:00
var slashIndex = hash.indexOf("/");
2018-07-08 16:18:51 +00:00
var title = hash.substring(0, slashIndex);
if (title.length)
QS("#doc-title").innerText = document.title = decodeURIComponent(
title.replace(/_/g, " ")
);
2018-06-10 14:41:58 +00:00
hash = hash.substring(slashIndex + 1);
2022-06-28 16:04:53 +00:00
updateLink(hash, {title});
2018-07-08 16:18:51 +00:00
if (hash.startsWith("?")) {
hash = hash.substring(1);
2022-06-30 15:49:56 +00:00
let durl = new bitty.DataURL(hash);
durl = await durl.decompress();
let htmlContent = durl.data;
setContent(htmlContent);
2018-05-28 01:05:32 +00:00
}
2018-05-26 00:16:21 +00:00
} else {
2018-07-08 16:18:51 +00:00
updateBodyClass();
2018-05-26 00:16:21 +00:00
}
};
function setContent(html) {
2022-06-29 15:34:40 +00:00
editor.innerHTML = html;
2018-07-08 16:18:51 +00:00
updateBodyClass();
2018-05-26 00:16:21 +00:00
}
2018-07-04 19:22:21 +00:00
function setFileName(name) {
2018-07-08 16:18:51 +00:00
QS("#doc-file").innerText = name;
2018-07-04 19:22:21 +00:00
if (name.length) {
2018-07-08 16:18:51 +00:00
setContent("");
document.body.classList.add("edited");
2018-07-04 19:22:21 +00:00
}
}
2022-06-28 16:04:53 +00:00
function updateBodyClass(hasContent) {
if (hasContent || importedFileData) {
2018-07-08 16:18:51 +00:00
document.body.classList.add("edited");
2018-05-29 14:05:09 +00:00
} else {
2018-07-08 16:18:51 +00:00
document.body.classList.remove("edited");
2018-05-29 14:05:09 +00:00
}
2022-06-29 15:34:40 +00:00
document.body.classList.toggle("filecontent", importedFileData != undefined);
2018-07-08 16:18:51 +00:00
document.body.classList.add("loaded");
2018-05-26 00:16:21 +00:00
}
2022-06-29 01:54:46 +00:00
async function handleDrop(e) {
console.log("drop", e)
2018-05-26 00:16:21 +00:00
e.preventDefault();
if (e.dataTransfer.files) {
let file = e.dataTransfer.files[0];
let extension = file.name.split(".").pop();
let reader = new FileReader();
2018-07-08 16:18:51 +00:00
reader.addEventListener(
"load",
2022-06-29 01:54:46 +00:00
async function() {
2018-07-08 16:18:51 +00:00
var url = reader.result;
2022-06-29 01:54:46 +00:00
let durl = new bitty.DataURL(url);
if (durl.mediatype == "application/octet-stream") {
durl.mediatype = "application/" + extension;
}
console.log(durl.mediatype)
2022-06-29 01:54:46 +00:00
durl = await durl.compress(bitty.GZIP_MARKER);
let ratio = durl.href.length / url.length;
console.log(`Compressed from ${url.length} to ${durl.href.length} bytes (${Math.round(ratio * 100)}%)`);
if (ratio <= 0.95) url = durl.href;
importedFileData = url;
2022-06-29 15:34:40 +00:00
updateLink(url, {title:file.name}, true);
2022-06-29 01:54:46 +00:00
setFileName(file.name);
2018-07-08 16:18:51 +00:00
},
false
);
2018-05-26 00:16:21 +00:00
reader.readAsDataURL(file);
}
2022-06-29 01:54:46 +00:00
document.body.classList.remove("dragging");
2018-05-26 00:16:21 +00:00
}
2018-06-10 14:41:58 +00:00
// TODO Command+Shift+T for title (H1), Command+Shift+H for headline (H2), Command+Shift+B for body text (remove any of the above)
2018-05-26 00:16:21 +00:00
function handleKey(e) {
var code = e.which;
2018-05-28 01:05:32 +00:00
var handled = false;
2018-05-26 00:16:21 +00:00
if (e.metaKey && e.altKey) {
2018-05-28 01:05:32 +00:00
handled = true;
2018-07-08 16:18:51 +00:00
if (code == "1".charCodeAt(0)) {
2018-05-26 00:16:21 +00:00
document.execCommand("formatBlock", true, "<h1>");
2018-07-08 16:18:51 +00:00
} else if (code == "2".charCodeAt(0)) {
2018-05-26 00:16:21 +00:00
document.execCommand("formatBlock", true, "<h2>");
2018-07-08 16:18:51 +00:00
} else if (code == 220) {
// \
2018-05-26 00:16:21 +00:00
document.execCommand("removeFormat");
2018-07-08 16:18:51 +00:00
} else if (code == "0".charCodeAt(0)) {
2018-05-26 00:16:21 +00:00
document.execCommand("formatBlock", true, "");
2018-05-28 01:05:32 +00:00
} else {
2018-07-08 16:18:51 +00:00
handled = false;
2018-05-26 00:16:21 +00:00
}
} else if (e.metaKey) {
2018-07-08 16:18:51 +00:00
if (code == "K".charCodeAt(0)) {
2018-05-28 01:05:32 +00:00
handled = true;
2018-07-08 16:18:51 +00:00
var url = prompt("Add a link", "");
if (url) {
document.execCommand("createLink", true, url);
}
2018-05-26 00:16:21 +00:00
}
2022-06-28 16:04:53 +00:00
} else if (code == 9 ) {
console.log("tab");
e.preventDefault();
2018-05-26 00:16:21 +00:00
}
2018-07-08 16:18:51 +00:00
if (handled) e.preventDefault();
}
2018-05-26 00:16:21 +00:00
2018-07-08 16:18:51 +00:00
var codepenRE = /(https:\/\/codepen\.io\/[\w]+\/(\w+)\/(\w+))/;
2018-05-28 01:05:32 +00:00
function handlePaste(e) {
2018-07-08 16:18:51 +00:00
var clipboard = window.clipboardData || e.clipboardData;
var text = clipboard.getData("Text") || clipboard.getData("text/plain");
2022-02-15 23:28:50 +00:00
if (!text) return;
let match = text.match(codepenRE);
if (match) {
2018-07-08 16:18:51 +00:00
fetchCodepen(match[0]);
2018-05-28 01:05:32 +00:00
}
2018-05-26 00:16:21 +00:00
}
2018-07-08 16:18:51 +00:00
var TEMPLATE_MARKER = "/*use-itty-bitty-template*/";
2018-05-28 01:05:32 +00:00
function fetchCodepen(url) {
2022-07-09 14:38:32 +00:00
Promise.all([
fetch(url + ".html"),
fetch(url + ".css"),
fetch(url + ".js"),
]).then(async function(responses) {
console.log("responses", responses);
let h = await responses[0].text();
let c = await responses[1].text();
let j = await responses[2].text();
console.log({h,c,j})
2018-07-08 16:18:51 +00:00
var useTemplate = c.indexOf(TEMPLATE_MARKER) >= 0;
var string =
2022-07-09 14:38:32 +00:00
'<style type="text/css">' + c + "</style>" +
2018-07-08 16:18:51 +00:00
h +
2022-07-09 14:38:32 +00:00
'<script type="text/javascript">' + j + "</script>";
console.log("string", string);
let url = `data:text/html;charset=utf-8,${encodeURIComponent(string)}`;
let durl = new bitty.DataURL(url);
durl = await durl.compress(bitty.GZIP_MARKER);
let ratio = durl.href.length / url.length;
console.log(`Compressed from ${url.length} to ${durl.href.length} bytes (${Math.round(ratio * 100)}%)`);
// setFileName("✒️" + "codepen");
var title = QS("#doc-title").innerText;
setTimeout(function() {
// var data = (useTemplate ? "" : DATA_PREFIX_BXZE) + zip;
// importedFileData = url;
updateLink(durl.href, {title:"CODEPEN"});
}, 300);
2018-05-28 01:05:32 +00:00
});
2018-05-26 00:16:21 +00:00
}
2022-06-28 16:04:53 +00:00
2018-05-26 00:16:21 +00:00
function handleInput(e) {
2022-06-28 16:04:53 +00:00
handleContentChange(e);
}
function getMetadata() {
let formData = new FormData(document.forms[0]);
var object = {};
formData.forEach((value, key) => object[key] = value);
return object;
}
2018-06-10 14:41:58 +00:00
2022-06-29 15:34:40 +00:00
async function handleContentChange() {
2022-06-28 16:04:53 +00:00
var text = editor.innerText;
let hasContent = text.trim().length > 0;
updateBodyClass(hasContent);
if (!hasContent) return;
var metadata = getMetadata();
var rawHTML = text.indexOf("</") > 0;
if (rawHTML) {
text = text.replace(/[ |\t]+/g, " ").replace(/> +</g, "> <");
} else {
2022-06-29 15:34:40 +00:00
text = editor.innerHTML;
2022-06-28 16:04:53 +00:00
}
if (text.trim().length) {
2022-06-30 14:24:44 +00:00
let url = `data:text/html;charset=utf-8,${encodeURIComponent(text)}`;
let durl = new bitty.DataURL(url)
2022-06-29 15:34:40 +00:00
durl = await durl.compress(bitty.GZIP_MARKER);
2022-06-30 14:24:44 +00:00
let ratio = durl.href.length / url.length;
console.log(`Compressed from ${url.length} to ${durl.href.length} bytes (${Math.round(ratio * 100)}%)`);
if (ratio <= 0.95) url = durl.href;
if (rawHTML) {
updateLink(url, metadata);
} else {
updateLink("?" + durl.data, metadata);
}
2022-06-28 16:04:53 +00:00
setFileName("");
} else if (importedFileData) {
updateLink(importedFileData, {title});
} else {
updateLink("");
}
2018-05-26 00:16:21 +00:00
}
var maxLengths = {
2018-06-10 14:41:58 +00:00
// "#twitter": 4088,
// "#bitly": 2048,
2018-07-08 16:18:51 +00:00
"#qrcode": 2953
};
2018-05-28 01:05:32 +00:00
2022-06-28 16:04:53 +00:00
let bittyLink = undefined;
function updateLink(url, metadata, push) {
let title = metadata.title;
// if (title) title = encodeURIComponent(title.trim().replace(/\s/g, "_"));
let includeMetadata = !metadata.includeMetadata;
let path = includeMetadata ? "/" : bitty.metadataToPath(metadata) ?? "/";
let prefix = includeMetadata ? bitty.encodePrettyComponent(title) : "";
2022-06-30 15:49:56 +00:00
console.log(`path [${path}]`)
2018-06-10 14:41:58 +00:00
if (url.length) {
2022-06-28 16:04:53 +00:00
url = path + "#" + prefix + "/" + url;
2018-06-10 14:41:58 +00:00
} else {
2018-07-08 16:18:51 +00:00
url = "/edit";
2018-06-10 14:41:58 +00:00
}
2022-06-28 16:04:53 +00:00
2022-06-30 15:49:56 +00:00
document.getElementById("doc-title-text").innerText = title.length ? title : "";
2022-06-28 16:04:53 +00:00
bittyLink = new URL(url, document.location).href;
document.getElementById("canonical").href = bittyLink;
2022-06-30 14:24:44 +00:00
2022-06-30 15:49:56 +00:00
console.log("previewing", bittyLink);
2022-06-29 15:34:40 +00:00
if(previewContent) QS("#preview-frame").src = bittyLink;
2022-06-28 16:04:53 +00:00
2018-07-08 16:18:51 +00:00
var hash = location.hash;
2022-06-28 16:04:53 +00:00
// if (push || !hash || !hash.length) {
// window.history.pushState(content.innerHTML, null, url);
// } else {
// window.history.replaceState(content.innerHTML, null, url);
// }
var length = bittyLink.length;
2018-05-28 17:20:08 +00:00
2018-07-08 16:18:51 +00:00
QS("#length").innerText = length + " bytes";
2022-06-28 16:04:53 +00:00
QS("#length").onclick = () => {
window.open(bittyLink, "_blank");
}
2018-05-26 00:16:21 +00:00
for (var key in maxLengths) {
2018-07-08 16:18:51 +00:00
var maxLength = maxLengths[key];
2018-05-29 14:05:09 +00:00
if (length > maxLength) {
2018-07-08 16:18:51 +00:00
QS(key).classList.add("invalid");
2018-05-29 14:05:09 +00:00
} else {
2018-07-08 16:18:51 +00:00
QS(key).classList.remove("invalid");
2018-05-29 14:05:09 +00:00
}
2018-07-08 16:18:51 +00:00
}
2018-05-26 00:16:21 +00:00
}
2022-05-17 15:37:07 +00:00
function share() {
navigator.share({
title: 'itty.bitty',
2022-06-28 16:04:53 +00:00
url: bittyLink
2022-05-17 15:37:07 +00:00
}).then(() => {
console.log('Thanks for sharing!');
})
.catch(console.error);
}
2018-05-26 00:16:21 +00:00
function makeQRCode() {
2018-07-08 16:18:51 +00:00
var url =
2022-05-07 20:41:58 +00:00
"https://chart.googleapis.com/chart?cht=qr&chs=512x512&chld=L|1&choe=UTF-8&chl=" +
2018-07-08 16:18:51 +00:00
encodeURIComponent(location.href);
this.href = url;
2018-05-26 00:16:21 +00:00
}
2022-06-28 16:04:53 +00:00
function upload() {
document.getElementById('file-input').click();
}
function toggleMenu(el) {
el.classList.toggle("menu-visible");
}
2018-05-26 00:16:21 +00:00
2022-06-28 16:04:53 +00:00
function toggleMetadata(e) {
if (e.target.closest(".menu")) return;
console.log(e)
QS("#md-contents").classList.toggle("menu-visible");
QS("#doc-title").classList.toggle("open");
QS("#md-title").focus();
2018-06-10 14:41:58 +00:00
}
2022-06-29 15:34:40 +00:00
2022-06-30 14:24:44 +00:00
2022-06-29 15:34:40 +00:00
let previewContent = false;
2022-06-30 14:24:44 +00:00
function togglePreview(flag) {
2022-06-29 15:34:40 +00:00
previewContent = !previewContent;
document.body.classList.toggle("preview", previewContent);
}
2022-06-30 15:49:56 +00:00
let formatContent = false;
function toggleFormat(flag) {
formatContent = !formatContent;
document.body.classList.toggle("format", formatContent);
}
2022-06-30 14:24:44 +00:00
2018-06-10 14:41:58 +00:00
function copyThenLink() {
2018-07-08 16:18:51 +00:00
copyLink();
return confirm("Copied your link to the clipboard. Paste it to share.");
2018-06-10 14:41:58 +00:00
}
2018-05-26 00:16:21 +00:00
function copyLink() {
2018-07-08 16:18:51 +00:00
var text = location.href;
2018-05-26 00:16:21 +00:00
var dummy = document.createElement("input");
document.body.appendChild(dummy);
dummy.value = text;
dummy.select();
document.execCommand("copy");
document.body.removeChild(dummy);
2018-06-10 14:41:58 +00:00
2018-07-08 16:18:51 +00:00
document.body.classList.add("copied");
2018-06-10 14:41:58 +00:00
setTimeout(function() {
2018-07-08 16:18:51 +00:00
document.body.classList.remove("copied");
}, 2000);
2018-05-26 00:16:21 +00:00
}
function saveLink() {
2018-07-08 16:18:51 +00:00
var url = "/" + location.hash;
2018-05-26 00:16:21 +00:00
window.history.pushState(null, null, url);
2018-07-08 16:18:51 +00:00
location.reload();
2018-05-26 00:16:21 +00:00
}
function tweetLink() {
2018-07-08 16:18:51 +00:00
var url =
"https://twitter.com/intent/tweet?url=" + encodeURIComponent(location.href);
window.open(url, "_blank");
2018-07-08 16:11:17 +00:00
return false;
2018-05-26 00:16:21 +00:00
}