editor performance

This commit is contained in:
ed 2020-05-13 23:26:11 +02:00
parent d7d1c3685c
commit da3f68c363
4 changed files with 177 additions and 46 deletions

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;
}

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,18 +186,45 @@ 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');
loader.parentNode.removeChild(loader);
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,7 +24,7 @@
}
#mw.preview,
#mtw.editor {
z-index: 3;
z-index: 5;
}
#mtw.single,
#mw.single {

View file

@ -18,11 +18,6 @@ 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 = [];
@ -62,8 +57,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 +74,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;
}
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;
})();
@ -296,7 +299,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');
@ -366,7 +369,7 @@ function setsel(s) {
dom_src.value = [s.pre, s.sel, s.post].join('');
dom_src.setSelectionRange(s.car, s.cdr);
try {
dom_src.oninput();
draw_md();
}
catch (ex) { }
}
@ -426,7 +429,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 = '';
@ -561,7 +564,7 @@ action_stack = (function () {
dom_src.value = ref;
dom_src.setSelectionRange(state.cursor, state.cursor);
ignore = true; // all browsers
dom_src.oninput();
draw_md();
return true;
}
@ -577,6 +580,10 @@ action_stack = (function () {
}
function undo() {
if (redos.length == 0) {
clearTimeout(sched_timer);
push();
}
return apply(undos, redos);
}