mirror of
https://github.com/arfct/itty-bitty.git
synced 2026-03-11 08:54:33 +00:00
Add quill editor
This commit is contained in:
parent
676b2564e6
commit
418b1ea843
3 changed files with 626 additions and 138 deletions
466
docs/edit.css
466
docs/edit.css
|
|
@ -2,32 +2,37 @@
|
|||
|
||||
html {
|
||||
--text-color:rgba(0, 0, 0, 0.87);
|
||||
--text-placeholder-color:rgba(0,0,0,0.54);
|
||||
--selected-text-color:#D42A20;
|
||||
--text-placeholder-color: rgba(0,0,0,0.54);
|
||||
--text-link-color:#0070e0;
|
||||
--background-color:white;
|
||||
--shadow-color:black;
|
||||
|
||||
color: var(--text-color);
|
||||
background-color: var(--background-color);
|
||||
--divider-color: rgba(0,0,0,0.15);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
--text-color:white;
|
||||
--text-placeholder-color:rgba(255,255,255,0.54);
|
||||
--selected-text-color:#e45149;
|
||||
--text-placeholder-color:rgba(255,255,255,0.33);
|
||||
--text-link-color:#40a0ff;
|
||||
--background-color:#111;
|
||||
--shadow-color:black;
|
||||
--divider-color: rgba(255,255,255,0.15);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0 auto;
|
||||
padding: 12vh 10vmin;
|
||||
max-width: 35em;
|
||||
padding: 10rem 6vmin 4vmin 6vmin;
|
||||
max-width: 45em;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
|
||||
line-height: 1.5em;
|
||||
min-height:100vh;
|
||||
box-sizing:
|
||||
border-box;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
|
@ -81,22 +86,54 @@ body.drag #content {
|
|||
|
||||
#doc-title {
|
||||
outline: none;
|
||||
min-width: 180px;
|
||||
/* min-width: 180px; */
|
||||
top: 0;
|
||||
position: absolute;
|
||||
padding: 0.5em 0;
|
||||
font-weight: bold;
|
||||
font-size:small;
|
||||
padding: 0 1em;
|
||||
font-weight: normal;
|
||||
opacity:0.33;
|
||||
user-select: none;
|
||||
cursor:
|
||||
default;
|
||||
margin-left: -1em;
|
||||
line-height: 2.5em;
|
||||
display: inline-block;
|
||||
/* margin-top: -31em; */
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
body.edited #doc-title:empty:before,
|
||||
#doc-title:focus:empty:before {
|
||||
#doc-title.open {
|
||||
background-color:var(--text-color);
|
||||
color:var(--background-color);
|
||||
opacity:1.0;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
|
||||
#doc-title:empty:after {
|
||||
content:"untitled";
|
||||
}
|
||||
|
||||
#doc-title.open #metadata-text {
|
||||
display:none;
|
||||
}
|
||||
|
||||
body:not(.edited) #doc-title {
|
||||
display:none;
|
||||
|
||||
}
|
||||
body.edited #doc-title:empty:after{
|
||||
content: "untitled";
|
||||
color: var(--text-placeholder-color);
|
||||
}
|
||||
|
||||
body.edited #doc-title.open:after {
|
||||
content:"metadata";
|
||||
color: var(--background-color);
|
||||
}
|
||||
|
||||
#doc-title:focus {
|
||||
border-bottom: 1px solid #ccc;
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
}
|
||||
|
||||
#doc-title:empty:before {
|
||||
|
|
@ -109,18 +146,57 @@ body.edited #doc-title:empty:before,
|
|||
background: transparent;
|
||||
}
|
||||
|
||||
#md-contents {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
/* top: 2em; */
|
||||
background-color: var(--background-color);
|
||||
padding: 1em;
|
||||
z-index: 100;
|
||||
border: 2px solid var(--text-color);
|
||||
box-shadow: 2px 1px var(--text-color);
|
||||
gap: 2px;
|
||||
font-weight: bold;
|
||||
margin-left: -1em;
|
||||
line-height:1.5em;
|
||||
/* min-width: 320px; */
|
||||
}
|
||||
input {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
border-left: -18px solid transparent;
|
||||
margin:-1px -2px;
|
||||
padding:1px 2px;
|
||||
min-width: 18em;
|
||||
color:var(--text-color);
|
||||
}
|
||||
input::placeholder {
|
||||
color:var(--text-placeholder-color);
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline:none;
|
||||
}
|
||||
#doc-file {
|
||||
border-radius: 1em;
|
||||
background-color: #fafafa;
|
||||
padding: 0.25em 1em;
|
||||
/* font-size: smaller; */
|
||||
margin-bottom: 2em;
|
||||
background-position: top center;
|
||||
background-repeat: no-repeat;
|
||||
|
||||
background-image: url("data:image/svg+xml,%0A%3Csvg width='128' height='128' viewBox='0 0 128 128' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cmask id='path-1-outside-1_116_2' maskUnits='userSpaceOnUse' x='27' y='15' width='74' height='98' fill='black'%3E%3Crect fill='white' x='27' y='15' width='74' height='98'/%3E%3Cpath d='M80 16H28V112H100V36L80 16Z'/%3E%3C/mask%3E%3Cpath d='M80 16H28V112H100V36L80 16Z' fill='white'/%3E%3Cpath d='M28 16V15H27V16H28ZM80 16L80.7071 15.2929L80.4142 15H80V16ZM28 112H27V113H28V112ZM100 112V113H101V112H100ZM100 36H101V35.5858L100.707 35.2929L100 36ZM28 17H80V15H28V17ZM29 112V16H27V112H29ZM100 111H28V113H100V111ZM99 36V112H101V36H99ZM100.707 35.2929L80.7071 15.2929L79.2929 16.7071L99.2929 36.7071L100.707 35.2929Z' fill='black' fill-opacity='0.15' mask='url(%23path-1-outside-1_116_2)'/%3E%3C/svg%3E%0A");
|
||||
|
||||
min-height: 160px;/* background-size: 128px; */text-align: center;padding-top: 128px;}
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='128' height='128' viewBox='0 0 128 128' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M112 126H20C18.8954 126 18 125.105 18 124V4C18 2.89543 18.8954 2 20 2H88.3431C88.8736 2 89.3823 2.21071 89.7574 2.58579L113.414 26.2426C113.789 26.6177 114 27.1264 114 27.6569V124C114 125.105 113.105 126 112 126Z' fill='white' stroke='black' stroke-width='4'/%3E%3Crect x='88' y='4' width='4' height='24' fill='black'/%3E%3Crect x='88' y='24' width='24' height='4' fill='black'/%3E%3C/svg%3E%0A");
|
||||
min-height: 160px;
|
||||
text-align: center;
|
||||
padding-top: 200px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-position: center 60px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#doc-file:empty {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -154,18 +230,6 @@ min-height: 160px;/* background-size: 128px; */text-align: center;padding-top: 1
|
|||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
@media only screen and (max-device-width: 480px) {
|
||||
#twitter,
|
||||
#copy {
|
||||
display: none;
|
||||
}
|
||||
#sharehint {
|
||||
display: block;
|
||||
}
|
||||
#menu-share-hint {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#toolbar button {
|
||||
vertical-align: baseline;
|
||||
|
|
@ -176,7 +240,8 @@ min-height: 160px;/* background-size: 128px; */text-align: center;padding-top: 1
|
|||
}
|
||||
|
||||
#length {
|
||||
opacity: 0.54;
|
||||
color:var(--text-placeholder-color);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/*body.edited #toolbar {
|
||||
|
|
@ -184,21 +249,81 @@ min-height: 160px;/* background-size: 128px; */text-align: center;padding-top: 1
|
|||
transform: translateY(0);
|
||||
}
|
||||
*/
|
||||
#toolbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 0.5em 1em;
|
||||
text-align: right;
|
||||
|
||||
font-size:small;
|
||||
font-weight: bold;
|
||||
/*transform: translateY(-2em);*/
|
||||
.toolbar {
|
||||
font-size: 0.25rem;
|
||||
}
|
||||
.toolbar .spacer {
|
||||
width:auto;
|
||||
flex-grow: 1;
|
||||
}
|
||||
#menubar {
|
||||
font-size:1rem;
|
||||
position: fixed;
|
||||
/* top: 0; */
|
||||
right: 0;
|
||||
left: 0;
|
||||
font-weight: 600;
|
||||
transition: transform 100ms ease-out;
|
||||
border-bottom-left-radius: 1em;
|
||||
cursor: pointer;
|
||||
/* height: 40px; */
|
||||
/* height: 2.5rem; */
|
||||
box-sizing: border-box;
|
||||
padding:0;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
border: none;
|
||||
/* background: var(--divider-color); */
|
||||
/* z-index: 100; */
|
||||
}
|
||||
|
||||
#menubar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left:0;
|
||||
right:0;
|
||||
/* max-height: 5rem; */
|
||||
padding:0;
|
||||
border:
|
||||
none;
|
||||
background-color:var(--background-color);
|
||||
display:
|
||||
flex;
|
||||
align-items: left;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap-reverse;
|
||||
/* height: 10rem; */
|
||||
padding-left: 4vmin;
|
||||
}
|
||||
#menubar > span {
|
||||
display: flex;
|
||||
/* outline:1px solid red; */
|
||||
width:
|
||||
auto;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
|
||||
.hide-before-edit,
|
||||
.ql-formats {
|
||||
opacity:1.0;
|
||||
transition:opacity 1s, transform 1s;
|
||||
}
|
||||
|
||||
body:not(.edited) .hide-before-edit {
|
||||
opacity:0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* body.loaded:not(.edited) #menubar .ql-formats{
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform:translateX(-2px)
|
||||
} */
|
||||
#copy-message {
|
||||
background: #2d2d2d;
|
||||
color: white;
|
||||
|
|
@ -216,43 +341,268 @@ body:not(.copied) #copy-message {
|
|||
|
||||
#menus-share-hint {
|
||||
content: "hello";
|
||||
padding-left: 1em;
|
||||
}
|
||||
#toolbar .disabled {
|
||||
#menubar .disabled {
|
||||
opacity: 0.333;
|
||||
}
|
||||
#toolbar a {
|
||||
margin-left: 0.5em;
|
||||
#menubar > div {
|
||||
/* margin-left: 0.5em; */
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
color: var(--text-color);
|
||||
vertical-align: middle;
|
||||
padding: 0 0.75em;
|
||||
line-height: 2.5em;
|
||||
/* height: 2em; */
|
||||
/* margin-top: 2px; */
|
||||
/* box-sizing: border-box; */
|
||||
/* display: inline-block; */
|
||||
height: 2.5rem;
|
||||
/* overflow: hidden; */
|
||||
/* width: 3.7rem; */
|
||||
z-index: 100;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
#toolbar a:hover {
|
||||
text-decoration: underline;
|
||||
#menubar .menu a:hover {
|
||||
/* text-decoration: underline; */
|
||||
background-color: var(--text-color);
|
||||
color: var(--background-color);
|
||||
}
|
||||
|
||||
.menu {
|
||||
width: auto;
|
||||
display: none;
|
||||
color:var(--text-color);
|
||||
color: var(--text-color);
|
||||
/* background-color: var(--background-color); */
|
||||
border: 2px solid currentColor;
|
||||
padding: 0.5em;
|
||||
padding: 0.5em 0;
|
||||
box-shadow: 2px 2px var(--shadow-color);
|
||||
margin-right: -0.5em;border-radius: 2px;
|
||||
margin-top: 0.7em;}
|
||||
margin-right: 2px;
|
||||
border-radius: 2px;
|
||||
margin-top: -2px;
|
||||
z-index: 300;
|
||||
/* top: 100px; */
|
||||
/* text-align: left; */
|
||||
/* float: right; */
|
||||
/* width: 10rem; */
|
||||
position: absolute;
|
||||
/* right: 1rem; */
|
||||
}
|
||||
|
||||
div#menu-contents {}
|
||||
|
||||
#toolbar.menu-visible .menu {
|
||||
display: block;
|
||||
#ql-header .ql-picker-label:before {
|
||||
/* min-width: 7.5rem !important; */
|
||||
max-width:20px;
|
||||
overflow:
|
||||
hidden;
|
||||
content:
|
||||
"H";
|
||||
font-size: 19px;
|
||||
line-height: 17px;
|
||||
}
|
||||
div.menu-contents {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#toolbar.menu-visible #menu {
|
||||
opacity: 0.54;
|
||||
.menu-visible .menu {
|
||||
display: block;
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
|
||||
#menubar > a:active,
|
||||
#menubar.menu-visible .menu-item {
|
||||
background-color: var(--text-color);
|
||||
color: var(--background-color);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#md-contents:not(.menu-visible) {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.menu-visible > .menu-item {
|
||||
background-color: var(--text-color);
|
||||
color: var(--background-color);
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
padding:0 0.5em;
|
||||
cursor:default;
|
||||
}
|
||||
|
||||
.menu hr {
|
||||
border: none;
|
||||
background-color: currentColor;
|
||||
height: 2px;
|
||||
background-color: transparent;
|
||||
height: 0;
|
||||
border-bottom: 1px dotted var(--text-color);
|
||||
cursor:default;
|
||||
}
|
||||
|
||||
|
||||
.inputrow {
|
||||
padding: 0.2em 0;
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
display: flex;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.inputrow:focus-within {
|
||||
border-bottom:2px solid var(--text-color);
|
||||
padding-bottom:calc(0.2em - 1px);
|
||||
}
|
||||
|
||||
input {
|
||||
font-size:16px;
|
||||
}
|
||||
label:first-child {
|
||||
min-width: 6em;
|
||||
display: block;
|
||||
margin-right: 1em;
|
||||
user-select:none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#editor .ql-editor {
|
||||
font-size: 16px;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media only screen and (max-device-width: 480px) {
|
||||
#twitter,
|
||||
#copy {
|
||||
display: none;
|
||||
}
|
||||
#sharehint {
|
||||
display: block;
|
||||
}
|
||||
#menu-share-hint {
|
||||
display: none;
|
||||
}
|
||||
.inputrow {
|
||||
flex-direction: column;
|
||||
}
|
||||
label {
|
||||
font-size:10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#editor .ql-toolbar {
|
||||
/* background-color: red; */
|
||||
}
|
||||
|
||||
#editor .ql-tooltip {
|
||||
background-color: var(--background-color);
|
||||
/* border: 2px solid var(--text-color); */
|
||||
box-shadow: 1px 2px 0 2px var(--text-color), 0 0 0px 2px var(--text-color);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#ql-editor .ql-bubble .ql-stroke {
|
||||
stroke: red;
|
||||
}
|
||||
|
||||
.ql-bubble .ql-stroke {
|
||||
stroke: currentColor !important;
|
||||
}
|
||||
|
||||
.ql-picker-item.ql-selected {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
.menu a {
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
line-height: 2em;
|
||||
text-align: left;
|
||||
display: block;
|
||||
padding: 0 1em;
|
||||
cursor:default;
|
||||
}
|
||||
|
||||
.menu {
|
||||
cursor:default;
|
||||
}
|
||||
|
||||
#menubar.ql-snow.ql-toolbar button,
|
||||
#menubar.ql-snow.ql-toolbar .ql-picker {
|
||||
height: 40px;
|
||||
min-width: 40px;
|
||||
float: none;
|
||||
vertical-align: middle;
|
||||
text-align: revert;
|
||||
width: 40px;
|
||||
}
|
||||
#menubar.ql-snow.ql-toolbar button,
|
||||
#menubar.ql-snow.ql-toolbar .ql-picker .ql-picker-label {
|
||||
/* background-size: 24px; */
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
div#editor.ql-container.ql-snow {
|
||||
border: none;
|
||||
}
|
||||
|
||||
#menubar.ql-snow .ql-color-picker .ql-picker-item {
|
||||
margin: 0;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
#menubar.ql-snow .ql-color-picker .ql-picker-options {
|
||||
width: 11.2rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
width: auto;
|
||||
flex-wrap: nowrap;
|
||||
gap: 1em;
|
||||
justify-self: flex-end;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
|
||||
#menubar .ql-toolbar,
|
||||
#menubar .ql-stroke,
|
||||
#menubar .ql-picker,
|
||||
#menubar.ql-snow a {
|
||||
color: var(--text-color) ;
|
||||
stroke: var(--text-color) ;
|
||||
}
|
||||
.ql-picker-label:hover,
|
||||
.ql-active {
|
||||
color: var(--text-color) !important;
|
||||
}
|
||||
|
||||
.ql-picker-options {
|
||||
background-color: var(--background-color) !important;
|
||||
}
|
||||
.ql-picker-item.ql-selected {
|
||||
color: #e45149 !important;
|
||||
}
|
||||
|
||||
#mainmenu .menu {
|
||||
right: 0;
|
||||
/* top: 1px; */
|
||||
}
|
||||
|
||||
div#mainmenu {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input#md-include {min-width: unset;height: 2em; width: 1em;margin-right: 1em;}
|
||||
|
||||
label:not(:first-child) {
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.ql-formats {
|
||||
opacity:0.0;
|
||||
}
|
||||
104
docs/edit.html
104
docs/edit.html
|
|
@ -2,42 +2,96 @@
|
|||
<html>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; connect-src https://codepen.io; font-src https://fonts.gstatic.com; style-src https://fonts.googleapis.com 'unsafe-inline' 'self'">
|
||||
<!-- <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://cdn.quilljs.com/ https://cdnjs.cloudflare.com/; connect-src https://codepen.io; font-src https://fonts.gstatic.com; style-src https://fonts.googleapis.com 'unsafe-inline' 'self'"> -->
|
||||
<meta id="themeColor" name="theme-color" content="#f1f1f1" media="(prefers-color-scheme: light)">
|
||||
<meta id="themeColorDark" name="theme-color" content="#121212" media="(prefers-color-scheme: dark)">
|
||||
<link rel="canonical" id="canonical" href="/edit">
|
||||
|
||||
<title>itty.bitty</title>
|
||||
<link rel="stylesheet" type="text/css" href="edit.css">
|
||||
<link id="favicon" rel="icon" href='/favicon.svg'>
|
||||
<link rel="stylesheet"
|
||||
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/styles/default.min.css">
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"></script>
|
||||
<script src="//cdn.quilljs.com/1.3.6/quill.min.js"></script>
|
||||
|
||||
<script src="/js/lzma/lzma_worker-min.js"></script>
|
||||
<script src="/js/jquery-3.3.1.min.js"></script>
|
||||
<script src="/edit.js" type="module"></script>
|
||||
|
||||
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined">
|
||||
|
||||
<body>
|
||||
|
||||
<div id="doc-title" contenteditable="true" placeholder="itty.bitty"></div>
|
||||
<div id="doc-file"></div>
|
||||
|
||||
<div id="placeholder">
|
||||
itty bitty things<br>
|
||||
can be conveyed in a link.<br>
|
||||
type here – and then share!<p> <a id="ib-info" href="http://about.bitty.site" target="_blank">about itty bitty</a><br>
|
||||
</div>
|
||||
<input id="file-input" type="file" name="name" style="display: none;" />
|
||||
<div id="copy-message">Link copied to clipboard.</div>
|
||||
<div id="content"></div>
|
||||
<span id="toolbar">
|
||||
<a target="_blank" id="length"></a> <a id="share" class="">share</a></div> <a target="_blank" class="" id="menu">menu</a>
|
||||
<div id="menu-contents" class="menu">
|
||||
<a id="copy">copy link</a>
|
||||
<hr>
|
||||
<span id="menus-share-hint" class="disabled">share via</span><br>
|
||||
<a target="_blank" id="qrcode" href="https://zxing.appspot.com/generator/">qr code</a><br>
|
||||
<a target="_blank" id="twitter" href="https://twitter.com/">twitter</a><br>
|
||||
<!-- <a target="_blank" id="bitly" href="https://bitly.com/">Bitly</a><br> -->
|
||||
|
||||
<hr>
|
||||
<!-- <a id="new" href="/edit">new site</a><br> -->
|
||||
<a id="about" target="_blank" href="http://about.bitty.site" id="about">about itty</a>
|
||||
<div id="placeholder"> itty bitty things<br> can be conveyed in a link.<br> type here – and then share!<p> <a id="ib-info" href="http://about.bitty.site" target="_blank">about itty bitty</a></div>
|
||||
<div id="doc-title" class="toolbarbutton" placeholder="untitled">
|
||||
<div class="title" id="doc-title-text">untitled</div>
|
||||
<form id="md-contents" class="menu">
|
||||
<div class="inputrow"><label for="md-title">title</label><input type="text" name="title" id="md-title" placeholder="untitled"></div>
|
||||
<div class="inputrow"><label for="md-include">metadata</label><input type="checkbox" name="includeMetadata" id="md-include" placeholder="untitled"><label for="md-include">include social metadata</label></div>
|
||||
<div class="inputrow"><label for="md-description">description</label><input type="text" name="description" id="md-description" placeholder="text"></div>
|
||||
<div class="inputrow"><label for="md-favicon">favicon</label><input type="text" name="favicon" id="md-favicon" placeholder="emoji or url"></div>
|
||||
<div class="inputrow"><label for="md-image">image</label><input type="text" name="image" id="md-image" placeholder="image url"></div>
|
||||
<div class="inputrow"><label for="md-password">password</label><input type="text" name="password" id="md-password" placeholder="encryption password"></div>
|
||||
</form>
|
||||
</div>
|
||||
</span>
|
||||
<div id="editor"></div>
|
||||
|
||||
<div class="toolbar" id="menubar">
|
||||
<!-- <div class="spacer"></div> -->
|
||||
|
||||
<span class="ql-formats">
|
||||
<button class="ql-bold"></button>
|
||||
<button class="ql-italic"></button>
|
||||
<button class="ql-underline"></button>
|
||||
<select class="ql-header" id="ql-header">HX</select>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-color"><option value="#F6D4D2"/><option value="#FAE4D3"/><option value="#FEF3D5"/><option value="#CFE8DC"/><option value="#CFE0E8"/><option value="#E1D7E8"/><option value="white"/><option value="#E99590"/><option value="#F3BB92"/><option value="#FDE095"/><option value="#86C6A8"/><option value="#86B1C6"/><option value="#B39BC6"/><option value="#A4A4A4"/><option value="#D42A20"/><option value="#E77625"/><option value="#FAC22B"/><option value="#0E8E51"/><option value="#0E638E"/><option value="#67378D"/><option value="#727272"/><option value="#BF0000"/><option value="#DB3F00"/><option value="#F8A600"/><option value="#005E12"/><option value="#00275E"/><option value="#2C005C"/><option value="#3D3D3D"/><option value="#860000"/><option value="#943E00"/><option value="#AE7400"/><option value="#00420D"/><option value="#001B42"/><option value="#1F0040"/><option value="black"/></select>
|
||||
<select class="ql-background"><option value="#F6D4D2"/><option value="#FAE4D3"/><option value="#FEF3D5"/><option value="#CFE8DC"/><option value="#CFE0E8"/><option value="#E1D7E8"/><option value="white"/><option value="#E99590"/><option value="#F3BB92"/><option value="#FDE095"/><option value="#86C6A8"/><option value="#86B1C6"/><option value="#B39BC6"/><option value="#A4A4A4"/><option value="#D42A20"/><option value="#E77625"/><option value="#FAC22B"/><option value="#0E8E51"/><option value="#0E638E"/><option value="#67378D"/><option value="#727272"/><option value="#BF0000"/><option value="#DB3F00"/><option value="#F8A600"/><option value="#005E12"/><option value="#00275E"/><option value="#2C005C"/><option value="#3D3D3D"/><option value="#860000"/><option value="#943E00"/><option value="#AE7400"/><option value="#00420D"/><option value="#001B42"/><option value="#1F0040"/><option value="black"/></select>
|
||||
<button class="ql-clean"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-align"></select>
|
||||
<button class="ql-blockquote"></button>
|
||||
<button class="ql-code-block"></button>
|
||||
<button class="ql-link"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button type="button" class="ql-list" value="bullet"></button>
|
||||
<button type="button" class="ql-list" value="ordered"></button>
|
||||
<button class="ql-indent" value="-1"></button>
|
||||
<button class="ql-indent" value="+1"></button>
|
||||
</span>
|
||||
|
||||
<div class="toolbar-right">
|
||||
<div id="length" class="hide-before-edit"></div>
|
||||
<div id="share" class="hide-before-edit menu-item">share</div>
|
||||
<div id="mainmenu">
|
||||
<div class="menu-item">menu</div>
|
||||
<div id="menu-contents" class="menu">
|
||||
<a id="copy">copy link</a>
|
||||
<hr>
|
||||
<span id="menus-share-hint" class="disabled">share via</span><br>
|
||||
<a target="_blank" id="qrcode" href="https://zxing.appspot.com/generator/">make qr code</a>
|
||||
<a target="_blank" id="twitter" href="https://twitter.com/">twitter</a>
|
||||
<!-- <a target="_blank" id="bitly" href="https://bitly.com/">Bitly</a><br> -->
|
||||
|
||||
<a id="upload">upload file…</a>
|
||||
<hr>
|
||||
<!-- <a id="new" href="/edit">new site</a><br> -->
|
||||
<a id="about" target="_blank" href="http://about.bitty.site" id="about">about itty</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="toolbar" id="formatbar">
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
194
docs/edit.js
194
docs/edit.js
|
|
@ -1,5 +1,7 @@
|
|||
import * as bitty from './bitty.js';
|
||||
|
||||
window.bitty = bitty;
|
||||
|
||||
var QS = document.querySelector.bind(document);
|
||||
var QSS = document.querySelectorAll.bind(document);
|
||||
|
||||
|
|
@ -14,9 +16,32 @@ var b = document.documentElement.setAttribute(
|
|||
navigator.userAgent
|
||||
);
|
||||
|
||||
var bindings = {}
|
||||
var quill = new Quill('#editor', {
|
||||
theme: 'snow',
|
||||
modules: {
|
||||
syntax: true,
|
||||
keyboard: { bindings },
|
||||
toolbar: "#menubar"
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
console.log("editor", editor);
|
||||
editor.autocomplete="off";
|
||||
var importedFileData = undefined;
|
||||
|
||||
var content = undefined;
|
||||
var content = editor;
|
||||
window.onload = function() {
|
||||
window.onpopstate = function(e) {
|
||||
setContent(e.state);
|
||||
|
|
@ -26,9 +51,9 @@ window.onload = function() {
|
|||
location.reload();
|
||||
};
|
||||
document.body.onclick = function(e) {
|
||||
if (e.target == document.body) content.focus();
|
||||
if (e.target == document.body) editor.focus();
|
||||
};
|
||||
content = document.getElementById("content");
|
||||
|
||||
content.ondragenter = function(e) {
|
||||
document.body.classList.add("drag");
|
||||
};
|
||||
|
|
@ -38,18 +63,21 @@ window.onload = function() {
|
|||
content.addEventListener("keydown", handleKey);
|
||||
content.addEventListener("keyup", handleInput);
|
||||
QS("#doc-title").addEventListener("keyup", handleInput);
|
||||
Array.from(document.getElementsByTagName("input")).forEach(i => i.addEventListener("keydown", handleInput))
|
||||
content.addEventListener("drop", handleDrop);
|
||||
content.addEventListener("paste", handlePaste);
|
||||
content.contentEditable = "true";
|
||||
content.focus();
|
||||
document.execCommand("selectAll", false, null);
|
||||
// content.contentEditable = "true";
|
||||
editor.focus();
|
||||
// document.execCommand("selectAll", false, null);
|
||||
QS("#qrcode").onclick = makeQRCode;
|
||||
|
||||
QS("#upload").onclick = upload;
|
||||
QS("#share").onclick = share;
|
||||
if (!navigator.share) QS("#share").style.display = "none"
|
||||
QS("#twitter").onclick = tweetLink;
|
||||
QS("#copy").onclick = copyLink;
|
||||
QS("#menu").onclick = toggleMenu;
|
||||
QS("#mainmenu").onclick = () => { toggleMenu(QS("#mainmenu"))};
|
||||
|
||||
QS("#doc-title").onclick = toggleMetadata;
|
||||
var hash = window.location.hash.substring(1);
|
||||
|
||||
if (hash.length) {
|
||||
|
|
@ -60,7 +88,7 @@ window.onload = function() {
|
|||
title.replace(/_/g, " ")
|
||||
);
|
||||
hash = hash.substring(slashIndex + 1);
|
||||
updateLink(hash, title);
|
||||
updateLink(hash, {title});
|
||||
if (hash.startsWith("?")) {
|
||||
hash = hash.substring(1);
|
||||
zipToString(hash, setContent);
|
||||
|
|
@ -83,9 +111,8 @@ function setFileName(name) {
|
|||
}
|
||||
}
|
||||
|
||||
function updateBodyClass() {
|
||||
var length = content.innerText.length;
|
||||
if (length || importedFileData) {
|
||||
function updateBodyClass(hasContent) {
|
||||
if (hasContent || importedFileData) {
|
||||
document.body.classList.add("edited");
|
||||
} else {
|
||||
document.body.classList.remove("edited");
|
||||
|
|
@ -114,7 +141,7 @@ function handleDrop(e) {
|
|||
if (e.altKey) url2 = url2.replace(DATA_PREFIX_BXZE, "!");
|
||||
|
||||
importedFileData = url2;
|
||||
updateLink(url2, file.name, true);
|
||||
updateLink(url2, {title:file.name}, true);
|
||||
setFileName(file.name);
|
||||
});
|
||||
},
|
||||
|
|
@ -151,6 +178,9 @@ function handleKey(e) {
|
|||
document.execCommand("createLink", true, url);
|
||||
}
|
||||
}
|
||||
} else if (code == 9 ) {
|
||||
console.log("tab");
|
||||
e.preventDefault();
|
||||
}
|
||||
if (handled) e.preventDefault();
|
||||
}
|
||||
|
|
@ -195,44 +225,67 @@ function fetchCodepen(url) {
|
|||
setTimeout(function() {
|
||||
var data = (useTemplate ? "" : DATA_PREFIX_BXZE) + zip;
|
||||
importedFileData = data;
|
||||
updateLink(data, title);
|
||||
updateLink(data, {title});
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function handleInput(e) {
|
||||
updateBodyClass();
|
||||
var text = content.innerText;
|
||||
var title = QS("#doc-title").innerText;
|
||||
handleContentChange(e);
|
||||
}
|
||||
function getMetadata() {
|
||||
let formData = new FormData(document.forms[0]);
|
||||
var object = {};
|
||||
formData.forEach((value, key) => object[key] = value);
|
||||
return object;
|
||||
}
|
||||
|
||||
var rawHTML = text.indexOf("</") > 0;
|
||||
if (rawHTML) {
|
||||
text = text.replace(/[ |\t]+/g, " ").replace(/> +</g, "> <");
|
||||
} else {
|
||||
text = content.innerHTML;
|
||||
}
|
||||
function handleContentChange() {
|
||||
|
||||
if (text.trim().length) {
|
||||
const t0 = performance.now();
|
||||
bitty.compressString(text, bitty.GZIP64_MARKER, function(zip) {
|
||||
const t1 = performance.now();
|
||||
bitty.compressString(text, bitty.LZMA64_MARKER, function(zip2) {
|
||||
const t2 = performance.now();
|
||||
console.debug("\ngz", Math.round(zip.length/text.length*100), Math.round((t1-t0)),"ms", "\nlz", Math.round(zip2.length/text.length*100), Math.round((t2-t1)), "ms");
|
||||
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 {
|
||||
text = content.innerHTML;
|
||||
}
|
||||
|
||||
|
||||
if (text.trim().length) {
|
||||
const t0 = performance.now();
|
||||
bitty.compressString(text, bitty.GZIP64_MARKER, function(zip) {
|
||||
const t1 = performance.now();
|
||||
// bitty.compressString(text, bitty.LZMA64_MARKER, function(zip2) {
|
||||
// const t2 = performance.now();
|
||||
// console.debug("gz", Math.round(zip.length/text.length*100) + "%", Math.round((t1-t0)),"ms");
|
||||
// console.debug("lz", Math.round(zip2.length/text.length*100) + "%", Math.round((t2-t1)), "ms");
|
||||
// });
|
||||
zip.replace("=","")
|
||||
|
||||
if (rawHTML) {
|
||||
updateLink(DATA_PREFIX_GZIP + zip, metadata);
|
||||
} else {
|
||||
updateLink("?" + zip, metadata);
|
||||
}
|
||||
});
|
||||
if (rawHTML) {
|
||||
updateLink(DATA_PREFIX_GZIP + zip, title);
|
||||
} else {
|
||||
updateLink("?" + zip, title);
|
||||
}
|
||||
});
|
||||
setFileName("");
|
||||
} else if (importedFileData) {
|
||||
updateLink(importedFileData, title);
|
||||
} else {
|
||||
updateLink("");
|
||||
}
|
||||
setFileName("");
|
||||
} else if (importedFileData) {
|
||||
updateLink(importedFileData, {title});
|
||||
} else {
|
||||
updateLink("");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var maxLengths = {
|
||||
|
|
@ -241,23 +294,43 @@ var maxLengths = {
|
|||
"#qrcode": 2953
|
||||
};
|
||||
|
||||
function updateLink(url, title, push) {
|
||||
if (title) title = encodeURIComponent(title.trim().replace(/\s/g, "_"));
|
||||
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) : "";
|
||||
|
||||
if (url.length) {
|
||||
url = "/#" + (title || "") + "/" + url;
|
||||
url = path + "#" + prefix + "/" + url;
|
||||
} else {
|
||||
url = "/edit";
|
||||
}
|
||||
|
||||
document.getElementById("doc-title-text").innerText = title.length ? title : "untitled";
|
||||
|
||||
bittyLink = new URL(url, document.location).href;
|
||||
|
||||
document.getElementById("canonical").href = bittyLink;
|
||||
|
||||
console.log({bittyLink});
|
||||
|
||||
|
||||
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;
|
||||
// if (push || !hash || !hash.length) {
|
||||
// window.history.pushState(content.innerHTML, null, url);
|
||||
// } else {
|
||||
// window.history.replaceState(content.innerHTML, null, url);
|
||||
// }
|
||||
var length = bittyLink.length;
|
||||
|
||||
QS("#length").innerText = length + " bytes";
|
||||
QS("#length").href = url;
|
||||
QS("#length").onclick = () => {
|
||||
window.open(bittyLink, "_blank");
|
||||
}
|
||||
for (var key in maxLengths) {
|
||||
var maxLength = maxLengths[key];
|
||||
if (length > maxLength) {
|
||||
|
|
@ -271,7 +344,7 @@ function updateLink(url, title, push) {
|
|||
function share() {
|
||||
navigator.share({
|
||||
title: 'itty.bitty',
|
||||
url: location.href
|
||||
url: bittyLink
|
||||
}).then(() => {
|
||||
console.log('Thanks for sharing!');
|
||||
})
|
||||
|
|
@ -283,9 +356,20 @@ function makeQRCode() {
|
|||
encodeURIComponent(location.href);
|
||||
this.href = url;
|
||||
}
|
||||
function upload() {
|
||||
document.getElementById('file-input').click();
|
||||
}
|
||||
|
||||
function toggleMenu() {
|
||||
QS("#toolbar").classList.toggle("menu-visible");
|
||||
function toggleMenu(el) {
|
||||
el.classList.toggle("menu-visible");
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
function copyThenLink() {
|
||||
copyLink();
|
||||
|
|
|
|||
Loading…
Reference in a new issue