Compare commits

..

6 Commits

Author SHA1 Message Date
ed
db5f07f164 v0.4.1 2020-05-14 01:08:42 +02:00
ed
e050e69a43 dodge osx-safari bugs 2020-05-14 00:28:10 +02:00
ed
27cb1d4fc7 fix scroll sync on osx ff/chrome 2020-05-14 00:03:01 +02:00
ed
5d6a740947 fix undo/redo cursor pos 2020-05-13 23:27:27 +02:00
ed
da3f68c363 editor performance 2020-05-13 23:26:11 +02:00
ed
d7d1c3685c sfx notes 2020-05-13 01:12:33 +02:00
6 changed files with 247 additions and 80 deletions

View File

@@ -59,13 +59,16 @@ launch either of them and it'll unpack and run copyparty, assuming you have pyth
pls note that `copyparty-sfx.sh` will fail if you rename `copyparty-sfx.py` to `copyparty.py` and keep it in the same folder because `sys.path` is funky
if you don't need all the features you can repack the sfx and save a bunch of space, tho currently the only removable feature is the opus/vorbis javascript decoder which is needed by apple devices to play foss audio files
if you don't need all the features you can repack the sfx and save a bunch of space; all you need is an sfx and a copy of this repo (nothing else to download or build, except for either msys2 or WSL if you're on windows)
* `724K` original size as of v0.4.0
* `256K` after `./scripts/make-sfx.sh re no-ogv`
* `164K` after `./scripts/make-sfx.sh re no-ogv no-cm`
steps to reduce the sfx size from `720 kB` to `250 kB` roughly:
* run one of the sfx'es once to unpack it
* `./scripts/make-sfx.sh re no-ogv` creates a new pair of sfx
the features you can opt to drop are
* `ogv`.js, the opus/vorbis decoder which is needed by apple devices to play foss audio files
* `cm`/easymde, the "fancy" markdown editor
no internet connection needed, just download an sfx and the repo zip (also if you're on windows use msys2)
for the `re`pack to work, first run one of the sfx'es once to unpack it
# install on android

View File

@@ -1,8 +1,8 @@
# coding: utf-8
VERSION = (0, 4, 0)
VERSION = (0, 4, 1)
CODENAME = "NIH"
BUILD_DT = (2020, 5, 13)
BUILD_DT = (2020, 5, 14)
S_VERSION = ".".join(map(str, VERSION))
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)

View File

@@ -83,6 +83,7 @@ h3 {
h1 a, h3 a, h5 a,
h2 a, h4 a, h6 a {
color: inherit;
display: block;
background: none;
border: none;
padding: 0;
@@ -239,7 +240,7 @@ blink {
}
#mn.undocked {
position: fixed;
padding: 1.2em 0 1em 1em;
padding: 1.7em 0 1.5em 1em;
box-shadow: 0 0 .5em rgba(0, 0, 0, 0.3);
background: #f7f7f7;
}
@@ -424,6 +425,16 @@ blink {
html.dark #mw {
scrollbar-color: #b80 #282828;
}
html.dark #toc::-webkit-scrollbar-track {
background: #282828;
}
html.dark #toc::-webkit-scrollbar {
background: #282828;
width: .8em;
}
html.dark #toc::-webkit-scrollbar-thumb {
background: #eb0;
}
html.dark #mn.undocked {
box-shadow: 0 0 .5em #555;
border: none;

View File

@@ -6,10 +6,30 @@ var dom_pre = document.getElementById('mp');
var dom_src = document.getElementById('mt');
var dom_navtgl = document.getElementById('navtoggle');
// chrome 49 needs this
var chromedbg = function () { console.log(arguments); }
// null-logger
var dbg = function () { };
// replace dbg with the real deal here or in the console:
// dbg = chromedbg
// dbg = console.log
function hesc(txt) {
return txt.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}
function cls(dom, name, add) {
var re = new RegExp('(^| )' + name + '( |$)');
var lst = (dom.getAttribute('class') + '').replace(re, "$1$2").replace(/ /, "");
dom.setAttribute('class', lst + (add ? ' ' + name : ''));
}
// add navbar
(function () {
var n = document.location + '';
@@ -28,17 +48,105 @@ function hesc(txt) {
dom_nav.innerHTML = nav.join('');
})();
// faster than replacing the entire html (chrome 1.8x, firefox 1.6x)
function copydom(src, dst, lv) {
var sc = src.childNodes,
dc = dst.childNodes;
if (sc.length !== dc.length) {
dbg("replace L%d (%d/%d) |%d|",
lv, sc.length, dc.length, src.innerHTML.length);
dst.innerHTML = src.innerHTML;
return;
}
var rpl = [];
for (var a = sc.length - 1; a >= 0; a--) {
var st = sc[a].tagName,
dt = dc[a].tagName;
if (st !== dt) {
dbg("replace L%d (%d/%d) type %s/%s", lv, a, sc.length, st, dt);
rpl.push(a);
continue;
}
var sa = sc[a].attributes || [],
da = dc[a].attributes || [];
if (sa.length !== da.length) {
dbg("replace L%d (%d/%d) attr# %d/%d",
lv, a, sc.length, sa.length, da.length);
rpl.push(a);
continue;
}
var dirty = false;
for (var b = sa.length - 1; b >= 0; b--) {
var name = sa[b].name,
sv = sa[b].value,
dv = dc[a].getAttribute(name);
if (name == "data-ln" && sv !== dv) {
dc[a].setAttribute(name, sv);
continue;
}
if (sv !== dv) {
dbg("replace L%d (%d/%d) attr %s [%s] [%s]",
lv, a, sc.length, name, sv, dv);
dirty = true;
break;
}
}
if (dirty)
rpl.push(a);
}
// TODO pure guessing
if (rpl.length > sc.length / 3) {
dbg("replace L%d fully, %s (%d/%d) |%d|",
lv, rpl.length, sc.length, src.innerHTML.length);
dst.innerHTML = src.innerHTML;
return;
}
// repl is reversed; build top-down
var nbytes = 0;
for (var a = rpl.length - 1; a >= 0; a--) {
var html = sc[rpl[a]].outerHTML;
dc[rpl[a]].outerHTML = html;
nbytes += html.length;
}
if (nbytes > 0)
dbg("replaced %d bytes L%d", nbytes, lv);
for (var a = 0; a < sc.length; a++)
copydom(sc[a], dc[a], lv + 1);
if (src.innerHTML !== dst.innerHTML) {
dbg("setting %d bytes L%d", src.innerHTML.length, lv);
dst.innerHTML = src.innerHTML;
}
}
function convert_markdown(md_text) {
marked.setOptions({
//headerPrefix: 'h-',
breaks: true,
gfm: true
});
var html = marked(md_text);
dom_pre.innerHTML = html;
var md_html = marked(md_text);
var md_dom = new DOMParser().parseFromString(md_html, "text/html").body;
// todo-lists (should probably be a marked extension)
var nodes = dom_pre.getElementsByTagName('input');
var nodes = md_dom.getElementsByTagName('input');
for (var a = nodes.length - 1; a >= 0; a--) {
var dom_box = nodes[a];
if (dom_box.getAttribute('type') !== 'checkbox')
@@ -58,9 +166,10 @@ function convert_markdown(md_text) {
html.substr(html.indexOf('>') + 1);
}
var manip_nodes = dom_pre.getElementsByTagName('*');
for (var a = manip_nodes.length - 1; a >= 0; a--) {
var el = manip_nodes[a];
// separate <code> for each line in <pre>
var nodes = md_dom.getElementsByTagName('pre');
for (var a = nodes.length - 1; a >= 0; a--) {
var el = nodes[a];
var is_precode =
el.tagName == 'PRE' &&
@@ -77,7 +186,36 @@ function convert_markdown(md_text) {
el.innerHTML = lines.join('');
}
// self-link headers
var id_seen = {},
dyn = md_dom.getElementsByTagName('*');
nodes = [];
for (var a = 0, aa = dyn.length; a < aa; a++)
if (/^[Hh]([1-6])/.exec(dyn[a].tagName) !== null)
nodes.push(dyn[a]);
for (var a = 0; a < nodes.length; a++) {
el = nodes[a];
var id = el.getAttribute('id'),
orig_id = id;
if (id_seen[id]) {
for (var n = 1; n < 4096; n++) {
id = orig_id + '-' + n;
if (!id_seen[id])
break;
}
el.setAttribute('id', id);
}
id_seen[id] = 1;
el.innerHTML = '<a href="#' + id + '">' + el.innerHTML + '</a>';
}
copydom(md_dom, dom_pre, 0);
}
function init_toc() {
var loader = document.getElementById('ml');
@@ -85,10 +223,8 @@ function init_toc() {
var anchors = []; // list of toc entries, complex objects
var anchor = null; // current toc node
var id_seen = {}; // taken IDs
var html = []; // generated toc html
var lv = 0; // current indentation level in the toc html
var re = new RegExp('^[Hh]([1-3])');
var manip_nodes_dyn = dom_pre.getElementsByTagName('*');
var manip_nodes = [];
@@ -97,7 +233,7 @@ function init_toc() {
for (var a = 0, aa = manip_nodes.length; a < aa; a++) {
var elm = manip_nodes[a];
var m = re.exec(elm.tagName);
var m = /^[Hh]([1-6])/.exec(elm.tagName);
var is_header = m !== null;
if (is_header) {
var nlv = m[1];
@@ -110,23 +246,7 @@ function init_toc() {
lv--;
}
var orig_id = elm.getAttribute('id');
var id = orig_id;
if (id_seen[id]) {
for (var n = 1; n < 4096; n++) {
id = orig_id + '-' + n;
if (!id_seen[id])
break;
}
elm.setAttribute('id', id);
}
id_seen[id] = 1;
var ahref = '<a href="#' + id + '">' +
elm.innerHTML + '</a>';
html.push('<li>' + ahref + '</li>');
elm.innerHTML = ahref;
html.push('<li>' + elm.innerHTML + '</li>');
if (anchor != null)
anchors.push(anchor);

View File

@@ -10,6 +10,9 @@
}
#mw {
left: calc(100% - 57em);
overflow-y: auto;
position: fixed;
bottom: 0;
}
@@ -21,15 +24,17 @@
}
#mw.preview,
#mtw.editor {
z-index: 3;
z-index: 5;
}
#mtw.single,
#mw.single {
left: calc((100% - 58em) / 2);
margin: 0;
left: 1em;
left: max(1em, calc((100% - 58em) / 2));
}
#mtw.single {
width: 57em;
width: min(57em, calc(100% - 2em));
}
@@ -42,25 +47,30 @@
color: #444;
background: #f7f7f7;
border: 1px solid #999;
outline: none;
padding: 0;
margin: 0;
font-family: 'consolas', monospace, monospace;
white-space: pre-wrap;
word-break: break-all;
word-break: break-word;
overflow-wrap: break-word;
word-wrap: break-word; /*ie*/
overflow-y: scroll;
line-height: 1.3em;
font-size: .9em;
position: relative;
scrollbar-color: #eb0 #f7f7f7;
}
html.dark #mt {
color: #eee;
background: #222;
border: 1px solid #777;
scrollbar-color: #b80 #282828;
}
#mtr {
position: absolute;
top: 1px;
left: 1px;
top: 0;
left: 0;
}
#save.force-save {
color: #400;
@@ -95,8 +105,4 @@ html.dark #helpbox {
border-width: 1px 0;
}
/* dbg:
#mt {
opacity: .5;
}
*/
# mt {opacity: .5;top:1px}

View File

@@ -18,16 +18,12 @@ var dom_ref = (function () {
})();
// replace it with the real deal in the console
var dbg = function () { };
// dbg = console.log
// line->scrollpos maps
var map_src = [];
var map_pre = [];
function genmap(dom) {
var ret = [];
var last_y = -1;
var parent_y = 0;
var parent_n = null;
var nodes = dom.querySelectorAll('*[data-ln]');
@@ -53,7 +49,14 @@ function genmap(dom) {
while (ln > ret.length)
ret.push(null);
ret.push(parent_y + n.offsetTop);
var y = parent_y + n.offsetTop;
if (y <= last_y)
//console.log('awawa');
continue;
//console.log('%d %d (%d+%d)', a, y, parent_y, n.offsetTop);
ret.push(y);
last_y = y;
}
return ret;
}
@@ -62,8 +65,10 @@ function genmap(dom) {
// input handler
var action_stack = null;
var nlines = 0;
(function () {
dom_src.oninput = function (e) {
var draw_md = (function () {
var delay = 1;
function draw_md() {
var t0 = new Date().getTime();
var src = dom_src.value;
convert_markdown(src);
@@ -77,16 +82,22 @@ var nlines = 0;
map_src = genmap(dom_ref);
map_pre = genmap(dom_pre);
var sb = document.getElementById('save');
var cl = (sb.getAttribute('class') + '').replace(/ disabled/, "");
if (src == server_md)
cl += ' disabled';
cls(document.getElementById('save'), 'disabled', src == server_md);
sb.setAttribute('class', cl);
var t1 = new Date().getTime();
delay = t1 - t0 > 150 ? 25 : 1;
}
var timeout = null;
dom_src.oninput = function (e) {
clearTimeout(timeout);
timeout = setTimeout(draw_md, delay);
if (action_stack)
action_stack.push();
}
dom_src.oninput();
};
draw_md();
return draw_md;
})();
@@ -96,7 +107,7 @@ redraw = (function () {
var y = (dom_hbar.offsetTop + dom_hbar.offsetHeight) + 'px';
dom_wrap.style.top = y;
dom_swrap.style.top = y;
dom_ref.style.width = (dom_src.offsetWidth - 4) + 'px';
dom_ref.style.width = getComputedStyle(dom_src).offsetWidth + 'px';
map_src = genmap(dom_ref);
map_pre = genmap(dom_pre);
dbg(document.body.clientWidth + 'x' + document.body.clientHeight);
@@ -296,7 +307,7 @@ function save_chk() {
last_modified = this.lastmod;
server_md = this.txt;
dom_src.oninput();
draw_md();
var ok = document.createElement('div');
ok.setAttribute('style', 'font-size:6em;font-family:serif;font-weight:bold;color:#cf6;background:#444;border-radius:.3em;padding:.6em 0;position:fixed;top:30%;left:calc(50% - 2em);width:4em;text-align:center;z-index:9001;transition:opacity 0.2s ease-in-out;opacity:1');
@@ -365,11 +376,8 @@ function setsel(s) {
}
dom_src.value = [s.pre, s.sel, s.post].join('');
dom_src.setSelectionRange(s.car, s.cdr);
try {
dom_src.oninput();
}
catch (ex) { }
}
// indent/dedent
@@ -426,7 +434,7 @@ function md_home(shift) {
function md_newline() {
var s = linebounds(true),
ln = s.md.substring(s.n1, s.n2),
m = /^[ \t#>+-]*(\* )?([0-9]+\. +)?/.exec(ln);
m = /^[ \t>+-]*(\* )?([0-9]+\. +)?/.exec(ln);
s.pre = s.md.substring(0, s.car) + '\n' + m[0];
s.sel = '';
@@ -501,12 +509,12 @@ document.getElementById('help').onclick = function (e) {
action_stack = (function () {
var undos = [];
var redos = [];
var sched_txt = '';
var sched_cpos = 0;
var sched_timer = null;
var ignore = false;
var ref = dom_src.value;
function diff(from, to) {
function diff(from, to, cpos) {
if (from === to)
return null;
@@ -532,14 +540,15 @@ action_stack = (function () {
return {
car: car,
cdr: ++p2,
txt: txt
txt: txt,
cpos: cpos
};
}
function undiff(from, change) {
return {
txt: from.substring(0, change.car) + change.txt + from.substring(change.cdr),
cursor: change.car + change.txt.length
cpos: change.cpos
};
}
@@ -549,19 +558,21 @@ action_stack = (function () {
if (src.length === 0)
return false;
var state = undiff(ref, src.pop()),
change = diff(ref, state.txt);
var patch = src.pop(),
applied = undiff(ref, patch),
cpos = patch.cpos - (patch.cdr - patch.car) + patch.txt.length,
reverse = diff(ref, applied.txt, cpos);
if (change === null)
if (reverse === null)
return false;
dst.push(change);
ref = state.txt;
dst.push(reverse);
ref = applied.txt;
ignore = true; // just some browsers
dom_src.value = ref;
dom_src.setSelectionRange(state.cursor, state.cursor);
dom_src.setSelectionRange(cpos, cpos);
ignore = true; // all browsers
dom_src.oninput();
draw_md();
return true;
}
@@ -571,12 +582,16 @@ action_stack = (function () {
return;
}
redos = [];
sched_txt = dom_src.value;
clearTimeout(sched_timer);
sched_cpos = dom_src.selectionEnd;
sched_timer = setTimeout(push, 500);
}
function undo() {
if (redos.length == 0) {
clearTimeout(sched_timer);
push();
}
return apply(undos, redos);
}
@@ -585,11 +600,12 @@ action_stack = (function () {
}
function push() {
var change = diff(ref, sched_txt, dom_src.selectionStart);
var newtxt = dom_src.value;
var change = diff(ref, newtxt, sched_cpos);
if (change !== null)
undos.push(change);
ref = sched_txt;
ref = newtxt;
dbg('undos(%d) redos(%d)', undos.length, redos.length);
if (undos.length > 0)
dbg(undos.slice(-1)[0]);
@@ -607,3 +623,14 @@ action_stack = (function () {
_ref: ref
}
})();
/*
document.getElementById('help').onclick = function () {
var c1 = getComputedStyle(dom_src).cssText.split(';');
var c2 = getComputedStyle(dom_ref).cssText.split(';');
var max = Math.min(c1.length, c2.length);
for (var a = 0; a < max; a++)
if (c1[a] !== c2[a])
console.log(c1[a] + '\n' + c2[a]);
}
*/