Initial commit

This commit is contained in:
Nicholas Jitkoff 2018-05-25 17:16:21 -07:00
commit 2aaa255682
15 changed files with 657 additions and 0 deletions

6
.firebaserc Normal file
View file

@ -0,0 +1,6 @@
{
"projects": {
"staging": "itty-bitty-app",
"default": "itty-bitty-app"
}
}

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

199
edit copy.js Normal file
View file

@ -0,0 +1,199 @@
var $ = document.querySelector.bind(document)
var $$ = document.querySelectorAll.bind(document)
window.onload = function() {
becomeEditable()
document.body.classList.toggle("edited", container.innerText.length)
document.ondrop = dropEvent;
var dragcount = 0;
content.ondragenter = function(e) {
console.log("enter", e)
dragcount++
document.body.classList.toggle("drag", dragcount)
event.preventDefault()
};
content.ondragleave = function(e) {
console.log("leave", e)
dragcount--
document.body.classList.toggle("drag", dragcount)
event.preventDefault()
};
};
var DATA_PREFIX = 'data:text/html;charset=utf-8,'
var DATA_PREFIX2 = 'data:text/html;charset=utf-8;base64,'
var iframe = undefined;
var container = undefined
function becomeEditable() {
container = document.getElementById("content")
container.addEventListener('input', inputEvent);
container.contentEditable = 'true';
container.focus();
container.onkeydown = handleKey;
var hash = window.location.hash.substring(1)
console.log("load", hash)
if (hash.length) {
if (hash.startsWith('!')) {
hash = hash.substring(1)
}
if (!hash.startsWith("data:")) hash = 'data:text/html;charset=utf-8;base64,' + hash;
dataToString(hash, function(html) {
container.innerHTML = html
})
}
}
function dropEvent(e) {
dragcount = 0
document.body.classList.remove("drag")
console.log("drop")
e.preventDefault();
if (e.dataTransfer.files) {
var file = e.dataTransfer.files[0]
var reader = new FileReader();
reader.addEventListener("load", function () {
var url = reader.result;
content.innerHTML = "📄 " + file.name
updateLink(url)
}, false);
reader.readAsDataURL(file);
}
}
function dataURItoBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var arrayBuffer = new ArrayBuffer(byteString.length);
var _ia = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteString.length; i++) {
_ia[i] = byteString.charCodeAt(i);
}
var dataView = new DataView(arrayBuffer);
var blob = new Blob([dataView], { type: mimeString });
return blob;
}
function handleKey(e) {
var code = e.which;
console.log(e)
if (e.metaKey && e.altKey) {
if (code == '1'.charCodeAt(0)) {
document.execCommand("formatBlock", true, "<h1>");
} else if (code == '2'.charCodeAt(0)) {
document.execCommand("formatBlock", true, "<h2>");
} else if (code == 220) { // \
console.log("remove")
document.execCommand("removeFormat");
} else if (code == '0'.charCodeAt(0)) {
console.log("clear block")
document.execCommand("formatBlock", true, "");
}
e.preventDefault()
} else if (e.metaKey) {
console.log("KEY", e, 'V'.charCodeAt(0), 'K'.charCodeAt(0) )
if (code == 'V'.charCodeAt(0)) {
// console.log("paste")
// e.preventDefault()
} else if (code == 'K'.charCodeAt(0)) {
console.log("link")
var url = prompt("Add a link", "")
document.execCommand("createLink", true, url);
}
}
};
function stringToData(string, callback) {
if (!string.length) return callback("");
var a = new FileReader();
a.onload = function(e) { callback(e.target.result.replace()) }
a.readAsDataURL(new Blob([string], {encoding:"UTF-8",type:"text/html;charset=UTF-8"}));
}
function dataToString(data, callback) {
var blob = dataURItoBlob(data)
// if (!data.length) return callback("");
var reader = new FileReader();
reader.onload = function(e) { callback(reader.result) }
reader.readAsText(blob);
}
function stripPrefix(url) {
if (url) {
var dataRE = /data:(text\/html[^,]*)(;base64),(.*)/
var match = url.match(dataRE);
if (match) return "!" + match[3];
}
return url;
}
function inputEvent(e) {
document.body.classList.toggle("edited", container.innerText.length)
var content = container.innerText
if (content.includes("</")) {
content = content.replace(/[\n|\t]+/g,' ').replace(/> +</g, '> <')
} else {
var title = content.split("\n")[0]
content = container.innerHTML
var meta = []
meta.push("<title>" + title + "</title>")
document.head.childNodes.forEach( function(el) {
if (el.attributes && !el.attributes.doNotEncode) meta.push(el.outerHTML);
});
}
console.log("Encoding:\n" + content)
stringToData(content, function(hash) {
var plain = encodeURIComponent(content)
// if (plain.length < hash.length) hash = plain;
updateLink(hash)
});
}
function updateLink(url) {
url = stripPrefix(url);
// $('#link').href = "/#" + url
// $('#link').innerText = $('#link').href
window.history.replaceState(null, null, "/#" + url);
var length = url.length
$('#length').innerText = (url.length - 1)/1000 + "/1000"
}
function makeLink() {
}
function makeQRCode() {
//https://developers.google.com/chart/infographics/docs/qr_codes
//https://zxing.org/w/chart?cht=qr&chs=350x350&chld=L&choe=UTF-8&chl=
// chart.googleapis.com/chart?chs=400x400&cht=
location.href = "https://zxing.org/w/chart?cht=qr&chs=548x548&chld=L|1&choe=UTF-8&chl=" + encodeURIComponent(location.href)
}
function makeShortLink() {
}
function textToClipboard (text) {
var dummy = document.createElement("input");
document.body.appendChild(dummy);
dummy.value = text;
dummy.select();
document.execCommand("copy");
document.body.removeChild(dummy);
}
function copyLink() {
textToClipboard(location.href)
}

105
edit.css Normal file
View file

@ -0,0 +1,105 @@
body {
margin: 0 auto;padding:12vmin 10vmin;
max-width:35em;
line-height:1.5em;
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
}
h1 { font-weight:400; }
h2 { font-weight:500; }
body.placeholder #placeholder {
display:block;
}
#placeholder {
display:none;
font-style:italic;
color:rgba(0,0,0,0.3);
pointer-events: none;
position:absolute;
}
body.drag #content {
outline: 3px dashed #ccc;
background-color:#fafafa;
border-radius:1em;
pointer-events: all;
}
#content, #placeholder{
width:100%;
margin:-1em; padding: 1em;
min-height:50vh
}
#content {
outline:none;
}
#content:focus {
outline-color: #ccc;
}
#content:empty:before {
content: attr(placeholder);
color: rgba(0, 0, 0, 0.2);
background: transparent;
}
.ib-file {
border-radius: 1em;
background: #ebeff9;
padding: 0.25em 1em;
font-size:smaller;
}
#ib-info {
pointer-events: all
}
/*#toolbar a#copy {
transition: transform 1s;
cursor: default;
}
#copy:active {
transform:translate(0, -0.5em);
transition: transform 100ms;
color:red;
}
*/
body.has-content #toolbar {
opacity:1.0;
transition: opacity 2s ease-out;
}
#toolbar {
opacity:0.0;
position:fixed;
top:0;
right:0;
padding:1em;
transition: opacity 218ms ease-in;
text-align:center;
background-color:white;
border-bottom-left-radius:1em;
}
#toolbar button {
vertical-align: baseline;
margin-bottom:11px;
}
#toolbar a.invalid {
text-decoration: line-through;
}
#length {
border:1px solid transparent
}
#toolbar a {font-size:11px; margin-right:0.5em; cursor: pointer; text-decoration:none; color:gray;}
#toolbar a:hover { text-decoration: underline; color:blue;}
*[contenteditable="true"]{display: inline-block;}

22
edit.html Normal file
View file

@ -0,0 +1,22 @@
<html>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width">
<meta http-equiv="Content-Security-Policy" content="default-src 'self';">
<title>itty bitty</title>
<script src="edit.js"></script>
<link rel="stylesheet" type="text/css" href="edit.css">
<body>
<span id="toolbar">
<a target="_blank" id="length"></a>
<a target="_blank" id="bitly" href="https://bitly.com/">Bitly</a>
<a target="_blank" id="qrcode" href="https://zxing.appspot.com/generator/">QR Code</a>
<a target="_blank" id="twitter" href="https://twitter.com/">Twitter</a>
<button id="copy">Copy Link</button><br>
</span>
<div id="placeholder"><br>
Share itty bitty things<br>with just a link.<br>
To get started, type here<br>or drop an HTML file.<br>
<p><a id="ib-info" href="http://about.bitty.site">Learn more</a></div>
<div id="content"></div>
</body>
</html>

192
edit.js Normal file
View file

@ -0,0 +1,192 @@
var $ = document.querySelector.bind(document)
var $$ = document.querySelectorAll.bind(document)
var DATA_PREFIX = 'data:text/html;base64,'
var DATA_PREFIX_8 = 'data:text/html;charset=utf-8;base64,'
var content = undefined
window.onload = function() {
window.onpopstate = function(e) { setContent(e.state) }
content = document.getElementById("content");
content.ondrop = dropEvent;
document.body.ondragenter = function(e) { document.body.classList.add("drag"); };
document.body.ondragleave = function(e) { document.body.classList.remove("drag"); };
document.body.onclick = function() { content.focus()};
content.oninput = handleInput;
content.onkeydown = handleKey;
content.contentEditable = 'true';
content.focus();
$('#qrcode').onclick = makeQRCode
$('#copy').onclick = copyLink
var hash = window.location.hash.substring(1)
if (hash.length) {
updateLink(hash)
if (hash.startsWith('!')) { hash = hash.substring(1) }
if (!hash.startsWith("data:")) { hash = 'data:text/html;charset=utf-8;base64,' + hash; }
dataToString(hash, setContent);
} else {
updateBodyClass()
}
};
function setContent(html) {
content.innerHTML = html
updateBodyClass()
}
function updateBodyClass() {
var length = content.innerText.length;
document.body.classList.toggle("has-content", length)
document.body.classList.toggle("placeholder", !length)
}
function dropEvent(e) {
e.preventDefault();
if (e.dataTransfer.files) {
var file = e.dataTransfer.files[0]
var reader = new FileReader();
reader.addEventListener("load", function () {
var url = reader.result;
url = url.replace(DATA_PREFIX, DATA_PREFIX_8)
updateLink(url, true)
setContent('&nbsp;<span class="ib-file" contentEditable="false">📄' + file.name + '</span><br><br>');
}, false);
reader.readAsDataURL(file);
}
document.body.classList.remove("drag")
}
function handleKey(e) {
var code = e.which;
if (e.metaKey && e.altKey) {
if (code == '1'.charCodeAt(0)) {
document.execCommand("formatBlock", true, "<h1>");
} else if (code == '2'.charCodeAt(0)) {
document.execCommand("formatBlock", true, "<h2>");
} else if (code == 220) { // \
document.execCommand("removeFormat");
} else if (code == '0'.charCodeAt(0)) {
document.execCommand("formatBlock", true, "");
}
e.preventDefault()
} else if (e.metaKey) {
if (code == 'K'.charCodeAt(0)) {
var url = prompt("Add a link", "")
if (url) { document.execCommand("createLink", true, url); };
}
}
};
function stringToData(string, callback) {
if (!string.length) return callback("");
var a = new FileReader();
a.onload = function(e) { callback(e.target.result.replace()) }
a.readAsDataURL(new Blob([string], {encoding:"UTF-8",type:"text/html;charset=UTF-8"}));
}
function dataToString(data, callback) {
var blob = dataURItoBlob(data)
var reader = new FileReader();
reader.onload = function(e) { callback(reader.result) }
reader.readAsText(blob);
}
function dataURItoBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var arrayBuffer = new ArrayBuffer(byteString.length);
var _ia = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteString.length; i++) {
_ia[i] = byteString.charCodeAt(i);
}
var dataView = new DataView(arrayBuffer);
var blob = new Blob([dataView], { type: mimeString });
return blob;
}
function stripPrefix(url) {
if (url) {
var dataRE = /data:(text\/html[^,]*)(;base64),(.*)/
var match = url.match(dataRE);
if (match) return "!" + match[3];
}
return url;
}
function handleInput(e) {
updateBodyClass();
var text = content.innerText
var strip = false;
if (text.includes("</")) {
text = text.replace(/[\n|\t]+/g,' ').replace(/> +</g, '> <')
} else {
var title = text.split("\n")[0]
text = content.innerHTML
strip = true
}
stringToData(text, function(hash) {
var plain = encodeURIComponent(text)
if (strip) hash = stripPrefix(hash);
updateLink(hash)
});
}
var maxLengths = {
"#twitter": 4088,
"#qrcode": 2610,
"#bitly": 2048,
}
function updateLink(url, push) {
url = "/#" + url
var hash = location.hash
if (push || !hash || !hash.length) {
window.history.pushState(content.innerHTML, null, url);
} else {
window.history.replaceState(content.innerHTML, null, url);
}
var length = location.href.length
$('#length').innerText = length + " bytes"
$('#length').href = url
for (var key in maxLengths) {
var maxLength = maxLengths[key]
$(key).classList.toggle("invalid", length > maxLength)
};
}
function makeShortLink() {
}
function makeQRCode() {
var url = "https://zxing.org/w/chart?cht=qr&chs=548x548&chld=L|1&choe=UTF-8&chl=" + encodeURIComponent(location.href)
this.href = url
// window.open(url, '_blank');
// return false;
//https://developers.google.com/chart/infographics/docs/qr_codes
}
function copyLink() {
var text = location.href
var dummy = document.createElement("input");
document.body.appendChild(dummy);
dummy.value = text;
dummy.select();
document.execCommand("copy");
document.body.removeChild(dummy);
}
function saveLink() {
var url = "/" + location.hash
window.history.pushState(null, null, url);
location.reload()
}
function tweetLink() {
var url = "https://twitter.com/intent/tweet?url=" + encodeURIComponent(location.href);
window.open(url, '_blank');
}

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

19
firebase.json Normal file
View file

@ -0,0 +1,19 @@
{
"hosting": {
"public": ".",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**",
"samples/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
],
"cleanUrls": true,
"trailingSlash": false
}
}

33
index.html Normal file
View file

@ -0,0 +1,33 @@
<html xmanifest="manifest.appcache">
<title>itty bitty</title>
<meta name="viewport" content="width=device-width">
<style type="text/css">
#edit { position:absolute; z-index:100;font-family:sans-serif;text-decoration:none;position:absolute;top:20px;right:20px; color:black; display:none; }
iframe { border:none;position:absolute;top:0;left:0;width:100%;height:100% }
</style>
<script type="text/javascript">
var hash = window.location.hash.substring(1)
if (hash.length) {
window.onload = function() {
if (hash.startsWith('!')) {
var link = document.getElementById('edit');
var editLink = "/edit#" + hash
link.onclick = function() {
location.href = editLink
}
link.style.display = "block";
var style = "PG1ldGEgY2hhcnNldD0idXRmLTgiPjxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgiPjxiYXNlIHRhcmdldD0iX3RvcCI+PHN0eWxlIHR5cGU9InRleHQvY3NzIj5ib2R5e21hcmdpbjowIGF1dG87cGFkZGluZzoxMnZtaW4gMTB2bWluO21heC13aWR0aDozNWVtO2xpbmUtaGVpZ2h0OjEuNWVtO2ZvbnQtZmFtaWx5OiAtYXBwbGUtc3lzdGVtLEJsaW5rTWFjU3lzdGVtRm9udCxzYW5zLXNlcmlmO3dvcmQtd3JhcDogYnJlYWstd29yZDt9PC9zdHlsZT4g"
hash = style + hash.substring(1)
}
if (!hash.startsWith("data:")) hash = 'data:text/html;charset=utf-8;base64,' + hash;
// location.href = hash
var iframe = document.getElementById('iframe');
iframe.src = hash;
document.body.appendChild(iframe);
}
} else {
location.href = "/edit"
}
</script>
<iframe id="iframe"></iframe><button id="edit">Edit</button>
</html>

5
manifest.appcache Normal file
View file

@ -0,0 +1,5 @@
CACHE MANIFEST
CACHE:
/
/favicon.ico

1
preamble.html Normal file
View file

@ -0,0 +1 @@
<meta charset="utf-8"><meta name="viewport" content="width=device-width"><base target="_top"><style type="text/css">body{margin:0 auto;padding:12vmin 10vmin;max-width:35em;line-height:1.5em;font-family: -apple-system,BlinkMacSystemFont,sans-serif;word-wrap: break-word;}</style>

15
samples/calc.html Normal file
View file

@ -0,0 +1,15 @@
<style type="text/css">
body {margin:4vmin auto; width:74vmin; background:#eee; font-size: 8vmin; font-family:sans-serif;}
input, div { height:2em; border-radius: 1em; font-size:1em;}
input {width:6.8em; padding: 0 1em; margin:0 0.4em 0.4em 0; text-align:right; border:none;}
div {width:2em; background:#fff; border:none; margin:0 0.4em 0.4em 0; display:inline-block; text-align:center; line-height:2em;}
div:active {background:red; color:white;}
div:nth-child(5n + 2) { background:#4A90E2; color:white; margin-right:0;}
</style>
<input type="text"><div onclick="field.value = ''">C</div><br><div>7</div><div>8</div><div>9</div><div>÷</div><br><div>4</div><div>5</div><div>6</div><div>×</div><br><div>1</div><div>2</div><div>3</div><div>-</div><br><div>.</div><div>0</div><div onclick="field.value = eval(field.value.replace('÷', '/').replace('×', '*'))">=</div><div>+</div>
<script type="text/javascript">
var field = document.querySelector('input');
document.querySelectorAll('div').forEach(function(div) {
if (!div.onclick) div.onclick = function(div) { field.value += this.innerText; }
});
</script>

39
samples/jabberwocky.html Normal file
View file

@ -0,0 +1,39 @@
<h1>Jabberwocky</h1>
<p>
Twas brillig, and the slithy toves<br>
Did gyre and gimble in the wabe:<br>
All mimsy were the borogoves,<br>
And the mome raths outgrabe.
</p><p>
“Beware the Jabberwock, my son!<br>
The jaws that bite, the claws that catch!<br>
Beware the Jubjub bird, and shun<br>
The frumious Bandersnatch!”
</p><p>
He took his vorpal sword in hand;<br>
Long time the manxome foe he sought—<br>
So rested he by the Tumtum tree<br>
And stood awhile in thought.
</p><p>
And, as in uffish thought he stood,<br>
The Jabberwock, with eyes of flame,<br>
Came whiffling through the tulgey wood,<br>
And burbled as it came!
</p><p>
One, two! One, two! And through and through<br>
The vorpal blade went snicker-snack!<br>
He left it dead, and with its head<br>
He went galumphing back.
</p><p>
“And hast thou slain the Jabberwock?<br>
Come to my arms, my beamish boy!<br>
O frabjous day! Callooh! Callay!”<br>
He chortled in his joy.
</p><p>
Twas brillig, and the slithy toves<br>
Did gyre and gimble in the wabe:<br>
All mimsy were the borogoves,<br>
And the mome raths outgrabe.
</p><p>
<b>Lewis Carroll</b>
</p>

18
samples/learnmore.html Normal file
View file

@ -0,0 +1,18 @@
<b>Itty bitty</b> lets you create tiny sites and apps that are stored in the link itself. To make them, you can edit this text directly, or drag an HTML file in to generate a link.
<p>Use it to share <a href="http://jabberwocky.bitty.site">poetry</a>, <a href="http://convert.bitty.site">quick reference</a>, <a href="https://calculator.bitty.app">tools</a>, and <a href="http://bully.bitty.site/">more</a>.
<p>When done, you can share these links through email, twitter, domain redirects and many other places you can post a link. When bookmarked on iOS, they work offline.
<p>Note: Each medium has a <a href="" onclick="document.getElementById('more').style.display = 'block'; return false;">maximum size</a> ranging from 1000 to 10000 characters.
<div id="more" style="display:none">
<table>
<tr><td>Twitter:&nbsp;</td><td>4088</td></tr>
<tr><td>Slack:</td><td>4000</td></tr>
<tr><td>Chrome:</td><td>10000</td></tr>
<tr><td>Bitly:</td><td>2048</td></tr>
<tr><td>QR Code:</td><td>2610</td></tr>
</table>
</div>

1
samples/preamble.html Normal file
View file

@ -0,0 +1 @@
<meta charset="utf-8"><meta name="viewport" content="width=device-width"><base target="_top"><style type="text/css">body {margin: 0 auto;padding:12vmin 10vmin; max-width:35em; line-height:1.5em; font-family: -apple-system, BlinkMacSystemFont, sans-serif;}</style>