mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
lots of stuff:
* show per-connection and per-transfer speeds * support multiple cookies in parser * set SameSite=Lax * restore macos support in sfx.sh * md-editor: add mojibake/unicode hunter * md-editor: add table formatter * md-editor: make bold bolder * md-editor: more hotkeys * md-editor: fix saving in fancy * md-editor: fix eof-scrolling in chrome * md-editor: fix text erasure with newline * md-editor: fix backspace behavior in gutter
This commit is contained in:
parent
a90c49b8fb
commit
69d3359e47
|
@ -24,6 +24,7 @@ class HttpCli(object):
|
|||
"""
|
||||
|
||||
def __init__(self, conn):
|
||||
self.t0 = time.time()
|
||||
self.conn = conn
|
||||
self.s = conn.s
|
||||
self.sr = conn.sr
|
||||
|
@ -86,7 +87,7 @@ class HttpCli(object):
|
|||
if "cookie" in self.headers:
|
||||
cookies = self.headers["cookie"].split(";")
|
||||
for k, v in [x.split("=", 1) for x in cookies]:
|
||||
if k != "cppwd":
|
||||
if k.strip() != "cppwd":
|
||||
continue
|
||||
|
||||
v = unescape_cookie(v)
|
||||
|
@ -307,10 +308,19 @@ class HttpCli(object):
|
|||
with open(path, "wb", 512 * 1024) as f:
|
||||
post_sz, _, sha_b64 = hashcopy(self.conn, reader, f)
|
||||
|
||||
self.log("wrote {}/{} bytes to {}".format(post_sz, remains, path))
|
||||
spd = self._spd(post_sz)
|
||||
self.log("{} wrote {}/{} bytes to {}".format(spd, post_sz, remains, path))
|
||||
self.reply("{}\n{}\n".format(post_sz, sha_b64).encode("utf-8"))
|
||||
return True
|
||||
|
||||
def _spd(self, nbytes, add=True):
|
||||
if add:
|
||||
self.conn.nbyte += nbytes
|
||||
|
||||
spd1 = get_spd(nbytes, self.t0)
|
||||
spd2 = get_spd(self.conn.nbyte, self.conn.t0)
|
||||
return spd1 + " " + spd2
|
||||
|
||||
def handle_post_multipart(self):
|
||||
self.parser = MultipartParser(self.log, self.sr, self.headers)
|
||||
self.parser.parse()
|
||||
|
@ -450,7 +460,9 @@ class HttpCli(object):
|
|||
except:
|
||||
self.log("failed to utime ({}, {})".format(path, times))
|
||||
|
||||
self.loud_reply("thank")
|
||||
spd = self._spd(post_sz)
|
||||
self.log("{} thank".format(spd))
|
||||
self.reply("thank")
|
||||
return True
|
||||
|
||||
def handle_login(self):
|
||||
|
@ -463,7 +475,7 @@ class HttpCli(object):
|
|||
msg = "naw dude"
|
||||
pwd = "x" # nosec
|
||||
|
||||
h = {"Set-Cookie": "cppwd={}; Path=/".format(pwd)}
|
||||
h = {"Set-Cookie": "cppwd={}; Path=/; SameSite=Lax".format(pwd)}
|
||||
html = self.conn.tpl_msg.render(h1=msg, h2='<a href="/">ack</a>', redir="/")
|
||||
self.reply(html.encode("utf-8"), headers=h)
|
||||
return True
|
||||
|
@ -575,6 +587,7 @@ class HttpCli(object):
|
|||
raise Pebkac(400, "empty files in post")
|
||||
|
||||
files.append([sz, sha512_hex])
|
||||
self.conn.nbyte += sz
|
||||
|
||||
except Pebkac:
|
||||
if fn != os.devnull:
|
||||
|
@ -602,7 +615,9 @@ class HttpCli(object):
|
|||
# truncated SHA-512 prevents length extension attacks;
|
||||
# using SHA-512/224, optionally SHA-512/256 = :64
|
||||
|
||||
self.log(msg)
|
||||
vspd = self._spd(sz_total, False)
|
||||
self.log("{} {}".format(vspd, msg))
|
||||
|
||||
if not nullwrite:
|
||||
# TODO this is bad
|
||||
log_fn = "up.{:.6f}.txt".format(t0)
|
||||
|
@ -885,6 +900,7 @@ class HttpCli(object):
|
|||
self.log(logmsg)
|
||||
return True
|
||||
|
||||
ret = True
|
||||
with open_func(*open_args) as f:
|
||||
remains = upper - lower
|
||||
f.seek(lower)
|
||||
|
@ -897,17 +913,17 @@ class HttpCli(object):
|
|||
if remains < len(buf):
|
||||
buf = buf[:remains]
|
||||
|
||||
remains -= len(buf)
|
||||
|
||||
try:
|
||||
self.s.sendall(buf)
|
||||
remains -= len(buf)
|
||||
except:
|
||||
logmsg += " \033[31m" + str(upper - remains) + "\033[0m"
|
||||
self.log(logmsg)
|
||||
return False
|
||||
ret = False
|
||||
break
|
||||
|
||||
self.log(logmsg)
|
||||
return True
|
||||
spd = self._spd((upper - lower) - remains)
|
||||
self.log("{}, {}".format(logmsg, spd))
|
||||
return ret
|
||||
|
||||
def tx_md(self, fs_path):
|
||||
logmsg = "{:4} {} ".format("", self.req)
|
||||
|
|
|
@ -4,6 +4,7 @@ from __future__ import print_function, unicode_literals
|
|||
import os
|
||||
import sys
|
||||
import ssl
|
||||
import time
|
||||
import socket
|
||||
|
||||
try:
|
||||
|
@ -41,6 +42,8 @@ class HttpConn(object):
|
|||
self.auth = hsrv.auth
|
||||
self.cert_path = hsrv.cert_path
|
||||
|
||||
self.t0 = time.time()
|
||||
self.nbyte = 0
|
||||
self.workload = 0
|
||||
self.log_func = hsrv.log
|
||||
self.log_src = "{} \033[36m{}".format(addr[0], addr[1]).ljust(26)
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import print_function, unicode_literals
|
|||
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import base64
|
||||
import struct
|
||||
import hashlib
|
||||
|
@ -349,6 +350,16 @@ def humansize(sz, terse=False):
|
|||
return ret.replace("iB", "").replace(" ", "")
|
||||
|
||||
|
||||
def get_spd(nbyte, t0, t=None):
|
||||
if t is None:
|
||||
t = time.time()
|
||||
|
||||
bps = nbyte / ((t - t0) + 0.001)
|
||||
s1 = humansize(nbyte).replace(" ", "\033[33m").replace("iB", "")
|
||||
s2 = humansize(bps).replace(" ", "\033[35m").replace("iB", "")
|
||||
return "{} \033[0m{}/s\033[0m".format(s1, s2)
|
||||
|
||||
|
||||
def undot(path):
|
||||
ret = []
|
||||
for node in path.split("/"):
|
||||
|
|
|
@ -109,8 +109,12 @@ h2 a, h4 a, h6 a {
|
|||
#mp ol>li {
|
||||
margin: .7em 0;
|
||||
}
|
||||
strong {
|
||||
color: #000;
|
||||
}
|
||||
p>em,
|
||||
li>em {
|
||||
li>em,
|
||||
td>em {
|
||||
color: #c50;
|
||||
padding: .1em;
|
||||
border-bottom: .1em solid #bbb;
|
||||
|
@ -289,6 +293,32 @@ blink {
|
|||
text-decoration: underline;
|
||||
border: none;
|
||||
}
|
||||
#mh a:hover {
|
||||
color: #000;
|
||||
background: #ddd;
|
||||
}
|
||||
#toolsbox {
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
background: #eee;
|
||||
height: 1.5em;
|
||||
padding: 0 .2em;
|
||||
margin: 0 .2em;
|
||||
position: absolute;
|
||||
}
|
||||
#toolsbox.open {
|
||||
height: auto;
|
||||
overflow: visible;
|
||||
background: #eee;
|
||||
box-shadow: 0 .2em .2em #ccc;
|
||||
padding-bottom: .2em;
|
||||
}
|
||||
#toolsbox a {
|
||||
display: block;
|
||||
}
|
||||
#toolsbox a+a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -332,8 +362,12 @@ blink {
|
|||
html.dark #m>ol {
|
||||
border-color: #555;
|
||||
}
|
||||
html.dark strong {
|
||||
color: #fff;
|
||||
}
|
||||
html.dark p>em,
|
||||
html.dark li>em {
|
||||
html.dark li>em,
|
||||
html.dark td>em {
|
||||
color: #f94;
|
||||
border-color: #666;
|
||||
}
|
||||
|
@ -371,6 +405,17 @@ blink {
|
|||
color: #ccc;
|
||||
background: none;
|
||||
}
|
||||
html.dark #mh a:hover {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
}
|
||||
html.dark #toolsbox {
|
||||
background: #222;
|
||||
}
|
||||
html.dark #toolsbox.open {
|
||||
box-shadow: 0 .2em .2em #069;
|
||||
border-radius: 0 0 .4em .4em;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 66em) {
|
||||
|
@ -541,7 +586,8 @@ blink {
|
|||
color: #240;
|
||||
}
|
||||
html.dark p>em,
|
||||
html.dark li>em {
|
||||
html.dark li>em,
|
||||
html.dark td>em {
|
||||
color: #940;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,14 @@
|
|||
<a id="save" href="?edit">save</a>
|
||||
<a id="sbs" href="#">sbs</a>
|
||||
<a id="nsbs" href="#">editor</a>
|
||||
<div id="toolsbox">
|
||||
<a id="tools" href="#">tools</a>
|
||||
<a id="fmt_table" href="#">prettify table (ctrl-k)</a>
|
||||
<a id="iter_uni" href="#">non-ascii: iterate (ctrl-u)</a>
|
||||
<a id="mark_uni" href="#">non-ascii: markup</a>
|
||||
<a id="cfg_uni" href="#">non-ascii: whitelist</a>
|
||||
<a id="help" href="#">help</a>
|
||||
</div>
|
||||
{%- else %}
|
||||
<a href="?edit">edit (basic)</a>
|
||||
<a href="?edit2">edit (fancy)</a>
|
||||
|
@ -46,6 +53,9 @@ write markdown (most html is 🙆 too)
|
|||
|
||||
## hotkey list
|
||||
* `Ctrl-S` to save
|
||||
* `Ctrl-E` to toggle mode
|
||||
* `Ctrl-K` to prettyprint a table
|
||||
* `Ctrl-U` to iterate non-ascii chars
|
||||
* `Ctrl-H` / `Ctrl-Shift-H` to create a header
|
||||
* `TAB` / `Shift-TAB` to indent/dedent a selection
|
||||
|
||||
|
|
|
@ -2,10 +2,16 @@
|
|||
var server_md = dom_src.value;
|
||||
|
||||
|
||||
// the non-ascii whitelist
|
||||
var esc_uni_whitelist = '\\n\\t\\x20-\\x7eÆØÅæøå';
|
||||
var js_uni_whitelist = eval('\'' + esc_uni_whitelist + '\'');
|
||||
|
||||
|
||||
// dom nodes
|
||||
var dom_swrap = document.getElementById('mtw');
|
||||
var dom_sbs = document.getElementById('sbs');
|
||||
var dom_nsbs = document.getElementById('nsbs');
|
||||
var dom_tbox = document.getElementById('toolsbox');
|
||||
var dom_ref = (function () {
|
||||
var d = document.createElement('div');
|
||||
d.setAttribute('id', 'mtr');
|
||||
|
@ -339,7 +345,7 @@ function savechk_cb() {
|
|||
server_md = this.txt;
|
||||
draw_md();
|
||||
toast('font-size:6em;font-family:serif;color:#cf6;width:4em;',
|
||||
'OK✔️<span style="font-size:.2em;color:#999">' + this.ntry + '</span>');
|
||||
'OK✔️<span style="font-size:.2em;color:#999;position:absolute">' + this.ntry + '</span>');
|
||||
}
|
||||
|
||||
function toast(style, msg) {
|
||||
|
@ -427,6 +433,9 @@ function setsel(s) {
|
|||
dom_src.value = [s.pre, s.sel, s.post].join('');
|
||||
dom_src.setSelectionRange(s.car, s.cdr, dom_src.selectionDirection);
|
||||
dom_src.oninput();
|
||||
// support chrome:
|
||||
dom_src.blur();
|
||||
dom_src.focus();
|
||||
}
|
||||
|
||||
|
||||
|
@ -500,7 +509,8 @@ function md_newline() {
|
|||
var s = linebounds(true),
|
||||
ln = s.md.substring(s.n1, s.n2),
|
||||
m1 = /^( *)([0-9]+)(\. +)/.exec(ln),
|
||||
m2 = /^[ \t>+-]*(\* )?/.exec(ln);
|
||||
m2 = /^[ \t>+-]*(\* )?/.exec(ln),
|
||||
drop = dom_src.selectionEnd - dom_src.selectionStart;
|
||||
|
||||
var pre = m2[0];
|
||||
if (m1 !== null)
|
||||
|
@ -512,7 +522,7 @@ function md_newline() {
|
|||
|
||||
s.pre = s.md.substring(0, s.car) + '\n' + pre;
|
||||
s.sel = '';
|
||||
s.post = s.md.substring(s.car);
|
||||
s.post = s.md.substring(s.car + drop);
|
||||
s.car = s.cdr = s.pre.length;
|
||||
setsel(s);
|
||||
return false;
|
||||
|
@ -522,11 +532,17 @@ function md_newline() {
|
|||
// backspace
|
||||
function md_backspace() {
|
||||
var s = linebounds(true),
|
||||
ln = s.md.substring(s.n1, s.n2),
|
||||
m = /^[ \t>+-]*(\* )?([0-9]+\. +)?/.exec(ln);
|
||||
o0 = dom_src.selectionStart,
|
||||
left = s.md.slice(s.n1, o0),
|
||||
m = /^[ \t>+-]*(\* )?([0-9]+\. +)?/.exec(left);
|
||||
|
||||
// if car is in whitespace area, do nothing
|
||||
if (/^\s*$/.test(left))
|
||||
return true;
|
||||
|
||||
// same if line is all-whitespace or non-markup
|
||||
var v = m[0].replace(/[^ ]/g, " ");
|
||||
if (v === m[0] || v.length !== ln.length)
|
||||
if (v === m[0] || v.length !== left.length)
|
||||
return true;
|
||||
|
||||
s.pre = s.md.substring(0, s.n1) + v;
|
||||
|
@ -540,8 +556,8 @@ function md_backspace() {
|
|||
|
||||
// paragraph jump
|
||||
function md_p_jump(down) {
|
||||
var ofs = dom_src.selectionStart;
|
||||
var txt = dom_src.value;
|
||||
var txt = dom_src.value,
|
||||
ofs = dom_src.selectionStart;
|
||||
|
||||
if (down) {
|
||||
while (txt[ofs] == '\n' && --ofs > 0);
|
||||
|
@ -562,6 +578,202 @@ function md_p_jump(down) {
|
|||
}
|
||||
|
||||
|
||||
function reLastIndexOf(txt, ptn, end) {
|
||||
var ofs = (typeof end !== 'undefined') ? end : txt.length;
|
||||
end = ofs;
|
||||
while (ofs >= 0) {
|
||||
var sub = txt.slice(ofs, end);
|
||||
if (ptn.test(sub))
|
||||
return ofs;
|
||||
|
||||
ofs--;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// table formatter
|
||||
function fmt_table(e) {
|
||||
if (e) e.preventDefault();
|
||||
//dom_tbox.setAttribute('class', '');
|
||||
|
||||
var txt = dom_src.value,
|
||||
ofs = dom_src.selectionStart,
|
||||
//o0 = txt.lastIndexOf('\n\n', ofs),
|
||||
//o1 = txt.indexOf('\n\n', ofs);
|
||||
o0 = reLastIndexOf(txt, /\n\s*\n/m, ofs),
|
||||
o1 = txt.slice(ofs).search(/\n\s*\n/m);
|
||||
// note \s contains \n but its fine
|
||||
|
||||
if (o0 < 0)
|
||||
o0 = 0;
|
||||
else {
|
||||
// seek past the hit
|
||||
var m = /\n\s*\n/m.exec(txt.slice(o0));
|
||||
o0 += m[0].length;
|
||||
}
|
||||
|
||||
o1 = o1 < 0 ? txt.length : o1 + ofs;
|
||||
|
||||
var err = 'cannot format table due to ',
|
||||
tab = txt.slice(o0, o1).split(/\s*\n/),
|
||||
re_ind = /^\s*/,
|
||||
ind = tab[1].match(re_ind)[0],
|
||||
r0_ind = tab[0].slice(0, ind.length),
|
||||
lpipe = tab[1].indexOf('|') < tab[1].indexOf('-'),
|
||||
rpipe = tab[1].lastIndexOf('|') > tab[1].lastIndexOf('-'),
|
||||
re_lpipe = lpipe ? /^\s*\|\s*/ : /^\s*/,
|
||||
re_rpipe = rpipe ? /\s*\|\s*$/ : /\s*$/;
|
||||
|
||||
for (var a = 0; a < tab.length; a++) {
|
||||
var ind2 = tab[a].match(re_ind)[0];
|
||||
if (ind != ind2 && a > 0) // the table can be a list entry or something, ignore [0]
|
||||
return alert(err + 'indentation mismatch on row 2 and ' + (a + 1) + ',\n' + tab[a]);
|
||||
|
||||
var t = tab[a].slice(ind.length);
|
||||
t = t.replace(re_lpipe, "");
|
||||
t = t.replace(re_rpipe, "");
|
||||
tab[a] = t.split(/\s*\|\s*/g);
|
||||
|
||||
if (a == 0)
|
||||
ncols = tab[a].length;
|
||||
|
||||
if (ncols != tab[a].length)
|
||||
return alert(err + 'num.columns mismatch on row 2 and ' + (a + 1) + '; ' + ncols + ' != ' + tab[a].length);
|
||||
}
|
||||
|
||||
var re_align = /^ *(:?)-+(:?) *$/;
|
||||
var align = [];
|
||||
for (var col = 0; col < tab[1].length; col++) {
|
||||
var m = tab[1][col].match(re_align);
|
||||
if (!m)
|
||||
return alert(err + 'invalid column specification, row 2, col ' + (col + 1) + ', [' + tab[1][col] + ']');
|
||||
|
||||
if (m[2]) {
|
||||
if (m[1])
|
||||
align.push('c');
|
||||
else
|
||||
align.push('r');
|
||||
}
|
||||
else
|
||||
align.push('l');
|
||||
}
|
||||
|
||||
var pad = [];
|
||||
var tmax = 0;
|
||||
for (var col = 0; col < ncols; col++) {
|
||||
var max = 0;
|
||||
for (var row = 0; row < tab.length; row++)
|
||||
max = Math.max(max, tab[row][col].length);
|
||||
|
||||
var s = '';
|
||||
for (var n = 0; n < max; n++)
|
||||
s += ' ';
|
||||
|
||||
pad.push(s);
|
||||
tmax = Math.max(max, tmax);
|
||||
}
|
||||
|
||||
var dashes = '';
|
||||
for (var a = 0; a < tmax; a++)
|
||||
dashes += '-';
|
||||
|
||||
var ret = [];
|
||||
for (var row = 0; row < tab.length; row++) {
|
||||
var ln = [];
|
||||
for (var col = 0; col < tab[row].length; col++) {
|
||||
var p = pad[col];
|
||||
var s = tab[row][col];
|
||||
|
||||
if (align[col] == 'l') {
|
||||
s = (s + p).slice(0, p.length);
|
||||
}
|
||||
else if (align[col] == 'r') {
|
||||
s = (p + s).slice(-p.length);
|
||||
}
|
||||
else {
|
||||
var pt = p.length - s.length;
|
||||
var pl = p.slice(0, Math.floor(pt / 2));
|
||||
var pr = p.slice(0, pt - pl.length);
|
||||
s = pl + s + pr;
|
||||
}
|
||||
|
||||
if (row == 1) {
|
||||
if (align[col] == 'l')
|
||||
s = dashes.slice(0, p.length);
|
||||
else if (align[col] == 'r')
|
||||
s = dashes.slice(0, p.length - 1) + ':';
|
||||
else
|
||||
s = ':' + dashes.slice(0, p.length - 2) + ':';
|
||||
}
|
||||
ln.push(s);
|
||||
}
|
||||
ret.push(ind + '| ' + ln.join(' | ') + ' |');
|
||||
}
|
||||
|
||||
// restore any markup in the row0 gutter
|
||||
ret[0] = r0_ind + ret[0].slice(ind.length);
|
||||
|
||||
ret = {
|
||||
"pre": txt.slice(0, o0),
|
||||
"sel": ret.join('\n'),
|
||||
"post": txt.slice(o1),
|
||||
"car": o0,
|
||||
"cdr": o0
|
||||
};
|
||||
setsel(ret);
|
||||
}
|
||||
|
||||
|
||||
// show unicode
|
||||
function mark_uni(e) {
|
||||
if (e) e.preventDefault();
|
||||
dom_tbox.setAttribute('class', '');
|
||||
|
||||
var txt = dom_src.value,
|
||||
ptn = new RegExp('([^' + js_uni_whitelist + ']+)', 'g');
|
||||
|
||||
mod = txt.replace(/\r/g, "").replace(ptn, "\u2588\u2770$1\u2771");
|
||||
|
||||
if (txt == mod) {
|
||||
alert('no results; no modifications were made');
|
||||
return;
|
||||
}
|
||||
dom_src.value = mod;
|
||||
}
|
||||
|
||||
|
||||
// iterate unicode
|
||||
function iter_uni(e) {
|
||||
if (e) e.preventDefault();
|
||||
|
||||
var txt = dom_src.value,
|
||||
ofs = dom_src.selectionDirection == "forward" ? dom_src.selectionEnd : dom_src.selectionStart,
|
||||
re = new RegExp('([^' + js_uni_whitelist + ']+)'),
|
||||
m = re.exec(txt.slice(ofs));
|
||||
|
||||
if (!m) {
|
||||
alert('no more hits from cursor onwards');
|
||||
return;
|
||||
}
|
||||
ofs += m.index;
|
||||
|
||||
dom_src.setSelectionRange(ofs, ofs + m[0].length, "forward");
|
||||
dom_src.oninput();
|
||||
// support chrome:
|
||||
dom_src.blur();
|
||||
dom_src.focus();
|
||||
}
|
||||
|
||||
|
||||
// configure whitelist
|
||||
function cfg_uni(e) {
|
||||
if (e) e.preventDefault();
|
||||
esc_uni_whitelist = prompt("unicode whitelist", esc_uni_whitelist);
|
||||
js_uni_whitelist = eval('\'' + esc_uni_whitelist + '\'');
|
||||
}
|
||||
|
||||
|
||||
// hotkeys / toolbar
|
||||
(function () {
|
||||
function keydown(ev) {
|
||||
|
@ -609,6 +821,19 @@ function md_p_jump(down) {
|
|||
if (!ctrl && !ev.shiftKey && kc == 8) {
|
||||
return md_backspace();
|
||||
}
|
||||
if (ctrl && (ev.code == "KeyK")) {
|
||||
fmt_table();
|
||||
return false;
|
||||
}
|
||||
if (ctrl && (ev.code == "KeyU")) {
|
||||
iter_uni();
|
||||
return false;
|
||||
}
|
||||
if (ctrl && (ev.code == "KeyE")) {
|
||||
dom_nsbs.click();
|
||||
//fmt_table();
|
||||
return false;
|
||||
}
|
||||
var up = ev.code == "ArrowUp" || kc == 38;
|
||||
var dn = ev.code == "ArrowDown" || kc == 40;
|
||||
if (ctrl && (up || dn)) {
|
||||
|
@ -622,8 +847,17 @@ function md_p_jump(down) {
|
|||
})();
|
||||
|
||||
|
||||
document.getElementById('tools').onclick = function (e) {
|
||||
if (e) e.preventDefault();
|
||||
var is_open = dom_tbox.getAttribute('class') != 'open';
|
||||
dom_tbox.setAttribute('class', is_open ? 'open' : '');
|
||||
};
|
||||
|
||||
|
||||
document.getElementById('help').onclick = function (e) {
|
||||
if (e) e.preventDefault();
|
||||
dom_tbox.setAttribute('class', '');
|
||||
|
||||
var dom = document.getElementById('helpbox');
|
||||
var dtxt = dom.getElementsByTagName('textarea');
|
||||
if (dtxt.length > 0) {
|
||||
|
@ -638,6 +872,12 @@ document.getElementById('help').onclick = function (e) {
|
|||
};
|
||||
|
||||
|
||||
document.getElementById('fmt_table').onclick = fmt_table;
|
||||
document.getElementById('mark_uni').onclick = mark_uni;
|
||||
document.getElementById('iter_uni').onclick = iter_uni;
|
||||
document.getElementById('cfg_uni').onclick = cfg_uni;
|
||||
|
||||
|
||||
// blame steen
|
||||
action_stack = (function () {
|
||||
var hist = {
|
||||
|
|
|
@ -160,8 +160,12 @@ h2 {
|
|||
.mdo ol>li {
|
||||
margin: .7em 0;
|
||||
}
|
||||
strong {
|
||||
color: #000;
|
||||
}
|
||||
p>em,
|
||||
li>em {
|
||||
li>em,
|
||||
td>em {
|
||||
color: #c50;
|
||||
padding: .1em;
|
||||
border-bottom: .1em solid #bbb;
|
||||
|
@ -253,8 +257,12 @@ html.dark .mdo>ul,
|
|||
html.dark .mdo>ol {
|
||||
border-color: #555;
|
||||
}
|
||||
html.dark strong {
|
||||
color: #fff;
|
||||
}
|
||||
html.dark p>em,
|
||||
html.dark li>em {
|
||||
html.dark li>em,
|
||||
html.dark td>em {
|
||||
color: #f94;
|
||||
border-color: #666;
|
||||
}
|
||||
|
|
|
@ -121,7 +121,7 @@ function save(mde) {
|
|||
fd.append("lastmod", (force ? -1 : last_modified));
|
||||
fd.append("body", txt);
|
||||
|
||||
var url = (document.location + '').split('?')[0] + '?raw';
|
||||
var url = (document.location + '').split('?')[0];
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', url, true);
|
||||
xhr.responseType = 'text';
|
||||
|
|
|
@ -32,8 +32,12 @@ dir="$(
|
|||
|
||||
# detect available pythons
|
||||
(IFS=:; for d in $PATH; do
|
||||
printf '%s\n' "$d"/python* "$d"/pypy* | tac;
|
||||
done) | grep -E '(python|pypy)[0-9\.-]*$' > $dir/pys || true
|
||||
printf '%s\n' "$d"/python* "$d"/pypy*;
|
||||
done) |
|
||||
(sed -E 's/(.*\/[^/0-9]+)([0-9]?[^/]*)$/\2 \1/' || cat) |
|
||||
(sort -nr || cat) |
|
||||
(sed -E 's/([^ ]*) (.*)/\2\1/' || cat) |
|
||||
grep -E '/(python|pypy)[0-9\.-]*$' >$dir/pys || true
|
||||
|
||||
# see if we made a choice before
|
||||
[ -z "$pybin" ] && pybin="$(cat $dir/py 2>/dev/null || true)"
|
||||
|
|
18
srv/test.md
18
srv/test.md
|
@ -1,5 +1,16 @@
|
|||
### hello world
|
||||
|
||||
* qwe
|
||||
* asd
|
||||
* zxc
|
||||
* 573
|
||||
* one
|
||||
* two
|
||||
|
||||
* |||
|
||||
|--|--|
|
||||
|listed|table|
|
||||
|
||||
```
|
||||
[72....................................................................]
|
||||
[80............................................................................]
|
||||
|
@ -21,6 +32,8 @@
|
|||
l[i]=1I;(){}o0O</> var foo = "$(`bar`)"; a's'd
|
||||
```
|
||||
|
||||
🔍🌽.📕.🍙🔎
|
||||
|
||||
[](#s1)
|
||||
[s1](#s1)
|
||||
[#s1](#s1)
|
||||
|
@ -121,6 +134,11 @@ a newline toplevel
|
|||
| a table | on the right |
|
||||
| second row | foo bar |
|
||||
|
||||
||
|
||||
--|:-:|-:
|
||||
a table | big text in this | aaakbfddd
|
||||
second row | centred | bbb
|
||||
|
||||
* list entry
|
||||
* [x] yes
|
||||
* [ ] no
|
||||
|
|
Loading…
Reference in a new issue