Skip to content

Instantly share code, notes, and snippets.

@myfonj
Last active May 7, 2024 07:57
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save myfonj/c8ce74bf549e19600026ce9022388df8 to your computer and use it in GitHub Desktop.
Save myfonj/c8ce74bf549e19600026ce9022388df8 to your computer and use it in GitHub Desktop.
HTML sandbox - editor in data URI (890 bytes v1, 1165 v2), with live preview and persistence.
data:text/html;charset=utf-8;verbatim,<!doctype html><title>HTML sandbox 2.0.2</title><meta name=color-scheme content="dark light"><meta name=viewport content=width=device-width,initial-scale=1><body style=margin:0;display:flex;height:100vh onload="OT=(DC=document).title,A.value=decodeURIComponent((L=location).hash.slice(1)||'')||A.value;T=W=0;E=RegExp('^'+(D='data:text/html;charset=utf-8,'));F=()=>{if(W!=(V=A.value))W=V,M=V.match(/(^data:.+?(;verbatim)?,)?([^]*)/),I.src=M[2]?V:(M[1]||D)+encodeURIComponent(M[3]),DC.title=NT=((TM=V.match(/<title\b[^]*?\x3E([^]*?)<\/title\b/m))&&(NT=TM[1])&&(NT=NT.trim())&&(DC.title=NT+' @ '+OT))||OT};F()"><textarea autocapitalize=off style=resize:horizontal;width:50vw;border:inset autofocus id=A onkeyup=clearTimeout(T);T=setTimeout(F,400) onblur=try{history.pushState({},NT,'\u0023'+(S=I.src.replace(E,'')))}catch(e){L.hash=S}><!doctype html><html lang="en"><title>%0A%0A</title><meta name="color-scheme" content="dark light">%0A<meta name="viewport" content="width=device-width, initial-scale=1">%0A<style>%0A%0A</style>%0A<body>%0A%0A<script>%0A%0A</script>%0A</textarea><iframe style=flex-grow:1;width:0;border:0 id=I>
data:text/html;charset=utf-8,<title>HTML sandbox 1.1.4</title><meta name=viewport content=width=device-width,initial-scale=1><body style=margin:0;display:flex;height:100vh;background:black onload="OT=(DC=document).title,A.value=decodeURIComponent((L=location).hash.slice(1)||'');T=W=0;E=RegExp('^'+(D='data:text/html;charset=utf-8,'));F=()=>{if(W!=(V=A.value))W=V,M=V.match(/(^data:.+?,)?([^]*)/),I.src=M[1]?V:D+encodeURIComponent(M[2]),DC.title=NT=((TM=V.match(/<title\b[^]*?\x3E([^]*?)<\/title\b/m))&&(NT=TM[1])&&(NT=NT.trim())&&(DC.title=NT+' @ '+OT))||OT};F()"><textarea style=resize:horizontal;width:50vw;border:inset;opacity:.7;color:snow;background:transparent autofocus id=A onkeyup=clearTimeout(T);T=setTimeout(F,400) onblur=try{history.pushState({},NT,'\u0023'+(S=I.src.replace(E,'')))}catch(e){L.hash=S}></textarea><iframe style=flex-grow:1;width:0;border:0;background:grey id=I>#%3C!doctype html%3E%3Chtml lang%3D""%3E%3Ctitle%3E%0A%0A%3C%2Ftitle%3E%0A%3Cmeta name%3D"viewport" content%3D"width%3Ddevice-width%2C initial-scale%3D1"%3E%0A%3Cstyle%3E%0A%3Aroot %7B background%3A dimgray%3B color%3A snow%3B %7D%0A%3Alink %7B color%3A aqua%3B %7D %3Avisited %7B color%3A lime%3B %7D%0A%0A%3C%2Fstyle%3E%0A%3Cbody%3E%0A%0A%3Cscript%3E%0A%0A%3C%2Fscript%3E%0A
@myfonj
Copy link
Author

myfonj commented Sep 17, 2019

Changelog

  • 2.0.2. (2024-05-07) • baked simple HTML boilerplate in (so the size increased by 235 bytes) • allow both color-scheme (no need to restrict)
  • 2.0.0 (2023-07-06) Early stage. • Ditched colours in styles for color-scheme="dark" (must be present in document as well); • made "verbatim" mode derived from explicit ;verbatim, in datauri prologue instead of sole prologue presence; • possibility to have default content even when used as physical file.
  • 1.1.4 (2021-12-17) Better title matching, slightly smaller
  • 1.1.3 (2021-12-16) editor title prefixed with contents of <title>...</title> from content, if any. New default HTML content template.
  • 1.1.2 (2021-09-16) better "explicit mime-type" mode: when data prefix is explicit, no further encoding is done
  • 1.1.1 (2020-11-30) fix persistence in Chrome

Features

  • live preview happens in iframe
  • previewed content is loaded as datauri (each preview is fresh load) after 400 ms of keyboard inactivity
  • warning: no infinite loop prevention
  • default mimetype is text/html
  • use explicit mime-type datauri prefix for testing others
    • e.g. data:application/xhtml+xml, / data:image/svg+xml, / data:text/plain,
  • (v2) use ;verbatim, in prefix to have plain non-encoded readable value in the hash (or more precisely, leave it on the browser)
    • then you have to URI-encode special chars #: %23 %: %25 yourself
    • you will lose line breaks
  • persistence happens in #hash after editor blur event
    • so enter favourite boilerplate and bookmark with keyword for really quick offline access
    • so you can force refresh page and content of the editor should remain
    • you can copy & paste URL to other browser to test support
  • use %s in bookmark URL for query substitution
    • so if your bookmark keyword is A, URL ends with #<b>%s</b> entering A bar in URLbar will produce <b>bar</b> in editor
  • to get just the "content document" you can
    • either use context menu of preview iframe This frame > Show only this frame (or Open frame in new tab/window) (Firefox)
    • or manually strip the editor "core" (everything before #) from URL

Technical restrictions

  • Because preview happens in an iframe that always loads newly constructed dataURI, (and the entire editor can be also served this way), it is in always anonymous (insecure) origin mode, what prevents for example access to locaStorage. The only mean to save some state across iframe reloads is to (ab)use window.name.

Notes

  • Scripting inside top-level dataURI document has peculiarities across browsers:
    • Firefox allows location.hash assignment but it throws on history.pushState;
    • Chrome throws on location.hash assignment (as it was top level navigation) but allows history.pushState. If my memory serves, old versions of Chrome treated hash as content continuation and wrote it into document, so data:text/html,content?search#hash had content content?search#hash and location.hash empty, now both browsers agree that content is content?search and location.hash is #hash. DataURI docs say nothing about #hash part.

Online variation of this, served over HTTP: https://myfonj.github.io/sandbox.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment