diff --git a/docs/index.js b/docs/index.js
index c7276bc..cac456d 100644
--- a/docs/index.js
+++ b/docs/index.js
@@ -32,7 +32,14 @@
}
function setThemeColor(color) {
- document.getElementById("themeColor").content = color;
+ let el = document.getElementById("themeColor");
+ if (!el) {
+ el = document.createElement("meta");
+ el.name = "theme-color";
+ el.id = "themeColor";
+ document.head.appendChild(el);
+ }
+ el.content = color;
}
function setFavicon(favicon) {
@@ -50,11 +57,42 @@
"application/ld+json": {script:"recipe"},
"text/rawhtml": {script:"parse"},
"javascript": {script:"bookmarklet"},
- "ipfs": {script:"ipfs"},
- "web3": {script:"web3", mode:"frame"},
+ "ipfs": {script:"ipfs", sandbox:"ipfs"},
+ "web3": {script:"web3"},
"text/directory": {script:"download", args: {extension:"vcf", filename:"contact"}}
}
+
+ function share(info) { // {title, text, url}
+ if (!info.url) info = {title:document.title, text:document.title, url:location.href};
+ console.log("Share", info);
+
+ if (navigator.share) {
+ navigator.share(info)
+ .then(() => { console.log('Thanks for sharing!');})
+ .catch(console.error);
+ } else {
+ copyLink(info)
+ }
+ }
+
+ function copyLink(info) {
+ var text = info.url;
+ var dummy = document.createElement("input");
+ document.body.appendChild(dummy);
+ dummy.value = text;
+ dummy.select();
+ document.execCommand("copy");
+ document.body.removeChild(dummy);
+
+ document.body.classList.add("copied");
+ setTimeout(function() {
+ document.body.classList.remove("copied");
+ }, 2000);
+ }
+
+
+
window.addEventListener("message", function(e) {
console.debug("Message:", e.origin, e.data)
if (e.data.title) document.title = e.data.title;
@@ -65,9 +103,12 @@
if (e.data.description) path.push("d/" + encodeURIComponent(e.data.description));
if (e.data.favicon) path.push("f/" + encodeURIComponent(e.data.favicon));
if (e.data.image) path.push("i/" + encodeURIComponent(btoa(e.data.image)));
- window.location.pathname = path.join('/');
+ window.location.pathname = path.join('/') + "/";
}
+ if (e.data.share) {
+ share(e.data.share);
+ }
if (e.data.replaceURL) {
if (e.data.compressURL) {
let durl = new bitty.DataURL(e.data.replaceURL);
@@ -141,7 +182,7 @@
if (fragment.startsWith("data:")) {
let info = bitty.infoForDataURL(fragment);
- renderer = info.params?.render ? {script:info.params.render} : renderers[info.mediatype];
+ renderer = info.params?.render ? {script:info.params.render, sandbox:"hash"} : renderers[info.mediatype];
type = "data:" + info.mediaType;
@@ -174,7 +215,7 @@
let renderer = renderers[scheme];
if (renderer) {
- return renderContentWithScript(false, renderer, title, info, fragment, fragment);
+ return renderContentWithScript({renderer, title, info, body:fragment, url:fragment});
}
return window.location.replace(fragment);
}
@@ -199,48 +240,47 @@
durl.dataPrefix = dataPrefix;
let dataURL = durl.href;
let dataContent = durl.rawData;
- // bitty.decompressDataURL(fragment, dataPrefix, function(dataURL, dataContent)
- {
- if (!dataURL) return;
- iframe.sandbox = "allow-downloads allow-scripts allow-forms allow-top-navigation allow-popups allow-modals allow-popups-to-escape-sandbox";
- if (isIE && renderMode == "data") renderMode = "frame";
- let contentTarget// = iframe.contentWindow.document;
- if (isWatch) {
- console.log("Rendering for watch")
- contentTarget = document;
- }
- console.log("Rendering mode: " + "\x1B[1m" + renderMode, durl)
- // dataURL = dataURL.replace("application/ld+json", "text/plain");
- if (renderMode == "download") {
- try {
- //let dl = document.querySelector("#download");
- let extension = title.split(".")
- let dl = el("a", {id: "download", href:dataURL, download: title},
- el("div", {id: "dl-image", innerText:extension.pop() ?? ""}),
- el("div", {id: "dl-name", innerText:"title"}),
- el("div", {id: "dl-button"}),
- )
- document.body.append(dl)
- document.body.classList.add("download");
- dl.click();
- return;
- } catch (e) {
- console.log("DL error", e)
- iframe.src = dataURL;
- }
- } else if (renderMode == "data") {
- iframe.src = dataURL;
- } else {
- bitty.dataToString(dataURL, function(content) {
- if (renderMode == "frame") {
- writeDocContent(contentTarget, content)
- } else if (renderMode == "script") {
- renderContentWithScript(contentTarget == document, renderer, title, info, content, dataURL);
- }
- });
- }
+ if (!dataURL) return;
+ iframe.sandbox = "allow-same-origin allow-downloads allow-scripts allow-forms allow-top-navigation allow-popups allow-modals allow-popups-to-escape-sandbox";
+
+ if (isIE && renderMode == "data") renderMode = "frame";
+ let contentTarget// = iframe.contentWindow.document;
+ if (isWatch) {
+ console.log("Rendering for watch")
+ contentTarget = document;
}
+ console.log("Rendering mode: " + "\x1B[1m" + renderMode, durl)
+ // dataURL = dataURL.replace("application/ld+json", "text/plain");
+ if (renderMode == "download") {
+ try {
+ //let dl = document.querySelector("#download");
+ let extension = title.split(".")
+ let dl = el("a", {id: "download", href:dataURL, download: title},
+ el("div", {id: "dl-image", innerText:extension.pop() ?? ""}),
+ el("div", {id: "dl-name", innerText:"title"}),
+ el("div", {id: "dl-button"}),
+ )
+ document.body.append(dl)
+ document.body.classList.add("download");
+ dl.click();
+ return;
+ } catch (e) {
+ console.log("DL error", e)
+ iframe.src = dataURL;
+ }
+ } else if (renderMode == "data") {
+ iframe.src = dataURL;
+ } else {
+ bitty.dataToString(dataURL, function(content) {
+ if (renderMode == "frame") {
+ writeDocContent(contentTarget, content)
+ } else if (renderMode == "script") {
+ renderContentWithScript({renderer, title, info, body:content, url:dataURL, overwrite:contentTarget == document});
+ }
+ });
+ }
+
let recordHistory = false
if (recordHistory) recordToHistory(title, type, description, window.location);
@@ -252,36 +292,50 @@
const SCRIPT_LOADER = ``
- function renderContentWithScript(overwrite, renderer, title, info, body, url) {
- let script = renderer.script;
- if (script.indexOf("/") == -1) script = location.origin + '/render/' + script + '.js'
+ async function renderContentWithScript(params) {
+
+ params.script = params.renderer.script;
+ params.originalURL = location.href;
+ if (params.script.indexOf("/") == -1) params.script = location.origin + '/render/' + params.script + '.js'
- if (overwrite) {
+ if (params.overwrite) {
+ // Overwrite the current page with the script. Dangerous, but required for browsers on apple watch to scroll.
let scriptEl = document.createElement("script")
scriptEl.src = "/render.js"
scriptEl.addEventListener('load', function(e) {
console.log("Loaded script", scriptEl.src);
- renderScriptContent({script, url, title, info, body}, "*");
+ renderScriptContent(params, "*");
});
document.head.appendChild(scriptEl);
-
- } else {
+ } else {
+ // Render in an iframe, either via sandboxed subdomain or a data url (disables storage APIs)
iframe.onload = (() => {
- iframe.contentWindow.postMessage({script, url, title, info, body, args:renderer.args}, "*");
+ iframe.contentWindow.postMessage(params, "*");
delete iframe.onload
});
// writeDocContent(iframe.contentWindow.document, SCRIPT_LOADER)
// iframe.srcdoc = SCRIPT_LOADER;
- iframe.src = "data:text/html," + SCRIPT_LOADER;
+
+ let src = location.origin + "/render";
+ let sandbox = params.renderer?.sandbox;
+ if (sandbox == "hash") { // Generate sandbox based off of body hash
+ let hash = await bitty.hashString(params.body);
+ src = src.replace("https://", "https://script-" + hash + ".");
+ } else if (sandbox) { // Use named sandbox
+ src = src.replace("https://", "https://" + sandbox + ".");
+ } else { // Render using data url (storage disabled)
+ src = "data:text/html," + SCRIPT_LOADER;
+ }
+ console.log("src", src)
+ iframe.src = src;
}
}
- function writeDocContent(doc, content) {
- doc.open();
- doc.write(content);
- doc.close();
- }
-
+function writeDocContent(doc, content) {
+doc.open();
+doc.write(content);
+doc.close();
+}
function recordToHistory() {
if (navigator.storage && navigator.storage.persist)