Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09557fbe83 | ||
|
|
1c0f44fa4e | ||
|
|
fc4d59d2d7 | ||
|
|
12345fbacc | ||
|
|
2e33c8d222 |
48
bin/copyparty-fuse.py
Executable file → Normal file
48
bin/copyparty-fuse.py
Executable file → Normal file
@@ -22,7 +22,9 @@ from urllib.parse import quote_from_bytes as quote
|
|||||||
try:
|
try:
|
||||||
from fuse import FUSE, FuseOSError, Operations
|
from fuse import FUSE, FuseOSError, Operations
|
||||||
except:
|
except:
|
||||||
print("\n could not import fuse;\n pip install fusepy\n")
|
print(
|
||||||
|
"\n could not import fuse; these may help:\n python3 -m pip install --user fusepy\n apt install libfuse\n modprobe fuse"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
@@ -34,9 +36,7 @@ usage:
|
|||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
sudo apk add fuse-dev
|
sudo apk add fuse-dev
|
||||||
python3 -m venv ~/pe/ve.fusepy
|
python3 -m pip install --user fusepy
|
||||||
. ~/pe/ve.fusepy/bin/activate
|
|
||||||
pip install fusepy
|
|
||||||
|
|
||||||
|
|
||||||
MB/s
|
MB/s
|
||||||
@@ -60,20 +60,21 @@ def boring_log(msg):
|
|||||||
def rice_tid():
|
def rice_tid():
|
||||||
tid = threading.current_thread().ident
|
tid = threading.current_thread().ident
|
||||||
c = struct.unpack(b"B" * 5, struct.pack(b">Q", tid)[-5:])
|
c = struct.unpack(b"B" * 5, struct.pack(b">Q", tid)[-5:])
|
||||||
return "".join("\033[1;37;48;5;{}m{:02x}".format(x, x) for x in c)
|
return "".join("\033[1;37;48;5;{}m{:02x}".format(x, x) for x in c) + "\033[0m"
|
||||||
|
|
||||||
|
|
||||||
def fancy_log(msg):
|
def fancy_log(msg):
|
||||||
print("{}\033[0m {}\n".format(rice_tid(), msg), end="")
|
print("{} {}\n".format(rice_tid(), msg), end="")
|
||||||
|
|
||||||
|
|
||||||
def null_log(msg):
|
def null_log(msg):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
log = boring_log
|
info = fancy_log
|
||||||
log = fancy_log
|
log = fancy_log
|
||||||
log = threadless_log
|
dbg = fancy_log
|
||||||
|
log = null_log
|
||||||
dbg = null_log
|
dbg = null_log
|
||||||
|
|
||||||
|
|
||||||
@@ -118,7 +119,7 @@ class Gateway(object):
|
|||||||
try:
|
try:
|
||||||
return self.conns[tid]
|
return self.conns[tid]
|
||||||
except:
|
except:
|
||||||
log("new conn [{}] [{}]".format(self.web_host, self.web_port))
|
info("new conn [{}] [{}]".format(self.web_host, self.web_port))
|
||||||
|
|
||||||
conn = http.client.HTTPConnection(self.web_host, self.web_port, timeout=260)
|
conn = http.client.HTTPConnection(self.web_host, self.web_port, timeout=260)
|
||||||
|
|
||||||
@@ -152,7 +153,7 @@ class Gateway(object):
|
|||||||
if r.status != 200:
|
if r.status != 200:
|
||||||
self.closeconn()
|
self.closeconn()
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"http error {} reading dir {} in {:x}".format(
|
"http error {} reading dir {} in {}".format(
|
||||||
r.status, web_path, rice_tid()
|
r.status, web_path, rice_tid()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -161,14 +162,14 @@ class Gateway(object):
|
|||||||
|
|
||||||
def download_file_range(self, path, ofs1, ofs2):
|
def download_file_range(self, path, ofs1, ofs2):
|
||||||
web_path = "/" + "/".join([self.web_root, path])
|
web_path = "/" + "/".join([self.web_root, path])
|
||||||
hdr_range = "bytes={}-{}".format(ofs1, ofs2)
|
hdr_range = "bytes={}-{}".format(ofs1, ofs2 - 1)
|
||||||
log("downloading {}".format(hdr_range))
|
log("downloading {}".format(hdr_range))
|
||||||
|
|
||||||
r = self.sendreq("GET", self.quotep(web_path), headers={"Range": hdr_range})
|
r = self.sendreq("GET", self.quotep(web_path), headers={"Range": hdr_range})
|
||||||
if r.status != http.client.PARTIAL_CONTENT:
|
if r.status != http.client.PARTIAL_CONTENT:
|
||||||
self.closeconn()
|
self.closeconn()
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"http error {} reading file {} range {} in {:x}".format(
|
"http error {} reading file {} range {} in {}".format(
|
||||||
r.status, web_path, hdr_range, rice_tid()
|
r.status, web_path, hdr_range, rice_tid()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -246,14 +247,14 @@ class CPPF(Operations):
|
|||||||
self.filecache = []
|
self.filecache = []
|
||||||
self.filecache_mtx = threading.Lock()
|
self.filecache_mtx = threading.Lock()
|
||||||
|
|
||||||
log("up")
|
info("up")
|
||||||
|
|
||||||
def clean_dircache(self):
|
def clean_dircache(self):
|
||||||
"""not threadsafe"""
|
"""not threadsafe"""
|
||||||
now = time.time()
|
now = time.time()
|
||||||
cutoff = 0
|
cutoff = 0
|
||||||
for cn in self.dircache:
|
for cn in self.dircache:
|
||||||
if cn.ts - now > 1:
|
if now - cn.ts > 1:
|
||||||
cutoff += 1
|
cutoff += 1
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
@@ -398,7 +399,7 @@ class CPPF(Operations):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
buf = self.gw.download_file_range(path, h_ofs, h_end - 1)
|
buf = self.gw.download_file_range(path, h_ofs, h_end)
|
||||||
ret = buf[-buf_ofs:] + cdr
|
ret = buf[-buf_ofs:] + cdr
|
||||||
|
|
||||||
elif car:
|
elif car:
|
||||||
@@ -416,7 +417,7 @@ class CPPF(Operations):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
buf = self.gw.download_file_range(path, h_ofs, h_end - 1)
|
buf = self.gw.download_file_range(path, h_ofs, h_end)
|
||||||
ret = car + buf[:buf_ofs]
|
ret = car + buf[:buf_ofs]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -438,7 +439,7 @@ class CPPF(Operations):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
buf = self.gw.download_file_range(path, h_ofs, h_end - 1)
|
buf = self.gw.download_file_range(path, h_ofs, h_end)
|
||||||
ret = buf[buf_ofs:buf_end]
|
ret = buf[buf_ofs:buf_end]
|
||||||
|
|
||||||
cn = CacheNode([path, h_ofs], buf)
|
cn = CacheNode([path, h_ofs], buf)
|
||||||
@@ -472,13 +473,16 @@ class CPPF(Operations):
|
|||||||
log("read {} @ {} len {} end {}".format(path, offset, length, ofs2))
|
log("read {} @ {} len {} end {}".format(path, offset, length, ofs2))
|
||||||
|
|
||||||
file_sz = self.getattr(path)["st_size"]
|
file_sz = self.getattr(path)["st_size"]
|
||||||
if ofs2 >= file_sz:
|
if ofs2 > file_sz:
|
||||||
ofs2 = file_sz - 1
|
ofs2 = file_sz
|
||||||
log("truncate to len {} end {}".format((ofs2 - offset) + 1, ofs2))
|
log("truncate to len {} end {}".format(ofs2 - offset, ofs2))
|
||||||
|
|
||||||
|
if file_sz == 0 or offset >= ofs2:
|
||||||
|
return b""
|
||||||
|
|
||||||
# toggle cache here i suppose
|
# toggle cache here i suppose
|
||||||
# return self.get_cached_file(path, offset, ofs2, file_sz)
|
# return self.get_cached_file(path, offset, ofs2, file_sz)
|
||||||
return self.gw.download_file_range(path, offset, ofs2 - 1)
|
return self.gw.download_file_range(path, offset, ofs2)
|
||||||
|
|
||||||
def getattr(self, path, fh=None):
|
def getattr(self, path, fh=None):
|
||||||
path = path.strip("/")
|
path = path.strip("/")
|
||||||
@@ -495,7 +499,7 @@ class CPPF(Operations):
|
|||||||
|
|
||||||
cn = self.get_cached_dir(dirpath)
|
cn = self.get_cached_dir(dirpath)
|
||||||
if cn:
|
if cn:
|
||||||
# log('cache ok')
|
log("cache ok")
|
||||||
dents = cn.data
|
dents = cn.data
|
||||||
else:
|
else:
|
||||||
log("cache miss")
|
log("cache miss")
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
VERSION = (0, 4, 1)
|
VERSION = (0, 4, 2)
|
||||||
CODENAME = "NIH"
|
CODENAME = "NIH"
|
||||||
BUILD_DT = (2020, 5, 14)
|
BUILD_DT = (2020, 5, 15)
|
||||||
|
|
||||||
S_VERSION = ".".join(map(str, VERSION))
|
S_VERSION = ".".join(map(str, VERSION))
|
||||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||||
|
|||||||
@@ -769,11 +769,18 @@ class HttpCli(object):
|
|||||||
else:
|
else:
|
||||||
upper = file_sz
|
upper = file_sz
|
||||||
|
|
||||||
if lower < 0 or lower >= file_sz or upper < 0 or upper > file_sz:
|
if upper > file_sz:
|
||||||
|
upper = file_sz
|
||||||
|
|
||||||
|
if lower < 0 or lower >= upper:
|
||||||
raise Exception()
|
raise Exception()
|
||||||
|
|
||||||
except:
|
except:
|
||||||
raise Pebkac(400, "invalid range requested: " + hrange)
|
err = "invalid range ({}), size={}".format(hrange, file_sz)
|
||||||
|
self.loud_reply(err, status=416, headers={
|
||||||
|
"Content-Range": "bytes */{}".format(file_sz)
|
||||||
|
})
|
||||||
|
return True
|
||||||
|
|
||||||
status = 206
|
status = 206
|
||||||
self.out_headers["Content-Range"] = "bytes {}-{}/{}".format(
|
self.out_headers["Content-Range"] = "bytes {}-{}/{}".format(
|
||||||
|
|||||||
@@ -80,8 +80,9 @@ class HttpSrv(object):
|
|||||||
"%s %s" % addr,
|
"%s %s" % addr,
|
||||||
"shut_rdwr err:\n {}\n {}".format(repr(sck), ex),
|
"shut_rdwr err:\n {}\n {}".format(repr(sck), ex),
|
||||||
)
|
)
|
||||||
if ex.errno not in [10038, 107, 57, 9]:
|
if ex.errno not in [10038, 10054, 107, 57, 9]:
|
||||||
# 10038 No longer considered a socket
|
# 10038 No longer considered a socket
|
||||||
|
# 10054 Foribly closed by remote
|
||||||
# 107 Transport endpoint not connected
|
# 107 Transport endpoint not connected
|
||||||
# 57 Socket is not connected
|
# 57 Socket is not connected
|
||||||
# 9 Bad file descriptor
|
# 9 Bad file descriptor
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ HTTPCODE = {
|
|||||||
404: "Not Found",
|
404: "Not Found",
|
||||||
405: "Method Not Allowed",
|
405: "Method Not Allowed",
|
||||||
413: "Payload Too Large",
|
413: "Payload Too Large",
|
||||||
|
416: "Requested Range Not Satisfiable",
|
||||||
422: "Unprocessable Entity",
|
422: "Unprocessable Entity",
|
||||||
500: "Internal Server Error",
|
500: "Internal Server Error",
|
||||||
501: "Not Implemented",
|
501: "Not Implemented",
|
||||||
@@ -309,18 +310,7 @@ def get_boundary(headers):
|
|||||||
def read_header(sr):
|
def read_header(sr):
|
||||||
ret = b""
|
ret = b""
|
||||||
while True:
|
while True:
|
||||||
if ret.endswith(b"\r\n\r\n"):
|
buf = sr.recv(1024)
|
||||||
break
|
|
||||||
elif ret.endswith(b"\r\n\r"):
|
|
||||||
n = 1
|
|
||||||
elif ret.endswith(b"\r\n"):
|
|
||||||
n = 2
|
|
||||||
elif ret.endswith(b"\r"):
|
|
||||||
n = 3
|
|
||||||
else:
|
|
||||||
n = 4
|
|
||||||
|
|
||||||
buf = sr.recv(n)
|
|
||||||
if not buf:
|
if not buf:
|
||||||
if not ret:
|
if not ret:
|
||||||
return None
|
return None
|
||||||
@@ -332,11 +322,15 @@ def read_header(sr):
|
|||||||
)
|
)
|
||||||
|
|
||||||
ret += buf
|
ret += buf
|
||||||
|
ofs = ret.find(b"\r\n\r\n")
|
||||||
|
if ofs < 0:
|
||||||
|
if len(ret) > 1024 * 64:
|
||||||
|
raise Pebkac(400, "header 2big")
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
if len(ret) > 1024 * 64:
|
sr.unrecv(ret[ofs + 4 :])
|
||||||
raise Pebkac(400, "header 2big")
|
return ret[:ofs].decode("utf-8", "surrogateescape").split("\r\n")
|
||||||
|
|
||||||
return ret[:-4].decode("utf-8", "surrogateescape").split("\r\n")
|
|
||||||
|
|
||||||
|
|
||||||
def undot(path):
|
def undot(path):
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ function cls(dom, name, add) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function static(obj) {
|
||||||
|
return JSON.parse(JSON.stringify(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// add navbar
|
// add navbar
|
||||||
(function () {
|
(function () {
|
||||||
var n = document.location + '';
|
var n = document.location + '';
|
||||||
|
|||||||
@@ -323,13 +323,26 @@ function save_chk() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// firefox bug: initial selection offset isn't cleared properly through js
|
||||||
|
var ff_clearsel = (function () {
|
||||||
|
if (navigator.userAgent.indexOf(') Gecko/') === -1)
|
||||||
|
return function () { }
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
var txt = dom_src.value;
|
||||||
|
var y = dom_src.scrollTop;
|
||||||
|
dom_src.value = '';
|
||||||
|
dom_src.value = txt;
|
||||||
|
dom_src.scrollTop = y;
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
// returns car/cdr (selection bounds) and n1/n2 (grown to full lines)
|
// returns car/cdr (selection bounds) and n1/n2 (grown to full lines)
|
||||||
function linebounds(just_car) {
|
function linebounds(just_car, greedy_growth) {
|
||||||
var car = dom_src.selectionStart,
|
var car = dom_src.selectionStart,
|
||||||
cdr = dom_src.selectionEnd;
|
cdr = dom_src.selectionEnd;
|
||||||
|
|
||||||
dbg(car, cdr);
|
|
||||||
|
|
||||||
if (just_car)
|
if (just_car)
|
||||||
cdr = car;
|
cdr = car;
|
||||||
|
|
||||||
@@ -337,11 +350,13 @@ function linebounds(just_car) {
|
|||||||
n1 = Math.max(car, 0),
|
n1 = Math.max(car, 0),
|
||||||
n2 = Math.min(cdr, md.length - 1);
|
n2 = Math.min(cdr, md.length - 1);
|
||||||
|
|
||||||
if (n1 < n2 && md[n1] == '\n')
|
if (greedy_growth !== true) {
|
||||||
n1++;
|
if (n1 < n2 && md[n1] == '\n')
|
||||||
|
n1++;
|
||||||
|
|
||||||
if (n1 < n2 && md[n2 - 1] == '\n')
|
if (n1 < n2 && md[n2 - 1] == '\n')
|
||||||
n2 -= 2;
|
n2 -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
n1 = md.lastIndexOf('\n', n1 - 1) + 1;
|
n1 = md.lastIndexOf('\n', n1 - 1) + 1;
|
||||||
n2 = md.indexOf('\n', n2);
|
n2 = md.indexOf('\n', n2);
|
||||||
@@ -375,7 +390,7 @@ function setsel(s) {
|
|||||||
s.cdr = s.pre.length + s.sel.length;
|
s.cdr = s.pre.length + s.sel.length;
|
||||||
}
|
}
|
||||||
dom_src.value = [s.pre, s.sel, s.post].join('');
|
dom_src.value = [s.pre, s.sel, s.post].join('');
|
||||||
dom_src.setSelectionRange(s.car, s.cdr);
|
dom_src.setSelectionRange(s.car, s.cdr, dom_src.selectionDirection);
|
||||||
dom_src.oninput();
|
dom_src.oninput();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,17 +431,32 @@ function md_header(dedent) {
|
|||||||
|
|
||||||
// smart-home
|
// smart-home
|
||||||
function md_home(shift) {
|
function md_home(shift) {
|
||||||
var s = linebounds(!shift),
|
var s = linebounds(false, true),
|
||||||
ln = s.md.substring(s.n1, s.n2),
|
ln = s.md.substring(s.n1, s.n2),
|
||||||
m = /^[ \t#>+-]*(\* )?([0-9]+\. +)?/.exec(ln),
|
dir = dom_src.selectionDirection,
|
||||||
home = s.n1 + m[0].length,
|
rev = dir === 'backward',
|
||||||
car = (s.car == home) ? s.n1 : home,
|
p1 = rev ? s.car : s.cdr,
|
||||||
cdr = shift ? s.cdr : car;
|
p2 = rev ? s.cdr : s.car,
|
||||||
|
home = 0,
|
||||||
|
lf = ln.lastIndexOf('\n') + 1,
|
||||||
|
re = /^[ \t#>+-]*(\* )?([0-9]+\. +)?/;
|
||||||
|
|
||||||
if (car > cdr)
|
if (rev)
|
||||||
car = [cdr, cdr = car][0];
|
home = s.n1 + re.exec(ln)[0].length;
|
||||||
|
else
|
||||||
|
home = s.n1 + lf + re.exec(ln.substring(lf))[0].length;
|
||||||
|
|
||||||
dom_src.setSelectionRange(car, cdr);
|
p1 = (p1 !== home) ? home : (rev ? s.n1 : s.n1 + lf);
|
||||||
|
if (!shift)
|
||||||
|
p2 = p1;
|
||||||
|
|
||||||
|
if (rev !== p1 < p2)
|
||||||
|
dir = rev ? 'forward' : 'backward';
|
||||||
|
|
||||||
|
if (!shift)
|
||||||
|
ff_clearsel();
|
||||||
|
|
||||||
|
dom_src.setSelectionRange(Math.min(p1, p2), Math.max(p1, p2), dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -434,9 +464,14 @@ function md_home(shift) {
|
|||||||
function md_newline() {
|
function md_newline() {
|
||||||
var s = linebounds(true),
|
var s = linebounds(true),
|
||||||
ln = s.md.substring(s.n1, s.n2),
|
ln = s.md.substring(s.n1, s.n2),
|
||||||
m = /^[ \t>+-]*(\* )?([0-9]+\. +)?/.exec(ln);
|
m1 = /^( *)([0-9]+)(\. +)/.exec(ln),
|
||||||
|
m2 = /^[ \t>+-]*(\* )?/.exec(ln);
|
||||||
|
|
||||||
s.pre = s.md.substring(0, s.car) + '\n' + m[0];
|
var pre = m2[0];
|
||||||
|
if (m1 !== null)
|
||||||
|
pre = m1[1] + (parseInt(m1[2]) + 1) + m1[3];
|
||||||
|
|
||||||
|
s.pre = s.md.substring(0, s.car) + '\n' + pre;
|
||||||
s.sel = '';
|
s.sel = '';
|
||||||
s.post = s.md.substring(s.car);
|
s.post = s.md.substring(s.car);
|
||||||
s.car = s.cdr = s.pre.length;
|
s.car = s.cdr = s.pre.length;
|
||||||
@@ -444,6 +479,25 @@ 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);
|
||||||
|
|
||||||
|
var v = m[0].replace(/[^ ]/g, " ");
|
||||||
|
if (v === m[0] || v.length !== ln.length)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
s.pre = s.md.substring(0, s.n1) + v;
|
||||||
|
s.sel = '';
|
||||||
|
s.post = s.md.substring(s.car);
|
||||||
|
s.car = s.cdr = s.pre.length;
|
||||||
|
setsel(s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// hotkeys / toolbar
|
// hotkeys / toolbar
|
||||||
(function () {
|
(function () {
|
||||||
function keydown(ev) {
|
function keydown(ev) {
|
||||||
@@ -484,6 +538,9 @@ function md_newline() {
|
|||||||
action_stack.redo();
|
action_stack.redo();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!ctrl && !ev.shiftKey && kc == 8) {
|
||||||
|
return md_backspace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
document.onkeydown = keydown;
|
document.onkeydown = keydown;
|
||||||
@@ -507,8 +564,10 @@ document.getElementById('help').onclick = function (e) {
|
|||||||
|
|
||||||
// blame steen
|
// blame steen
|
||||||
action_stack = (function () {
|
action_stack = (function () {
|
||||||
var undos = [];
|
var hist = {
|
||||||
var redos = [];
|
un: [],
|
||||||
|
re: []
|
||||||
|
};
|
||||||
var sched_cpos = 0;
|
var sched_cpos = 0;
|
||||||
var sched_timer = null;
|
var sched_timer = null;
|
||||||
var ignore = false;
|
var ignore = false;
|
||||||
@@ -553,7 +612,7 @@ action_stack = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function apply(src, dst) {
|
function apply(src, dst) {
|
||||||
dbg('undos(%d) redos(%d)', undos.length, redos.length);
|
dbg('undos(%d) redos(%d)', hist.un.length, hist.re.length);
|
||||||
|
|
||||||
if (src.length === 0)
|
if (src.length === 0)
|
||||||
return false;
|
return false;
|
||||||
@@ -581,36 +640,36 @@ action_stack = (function () {
|
|||||||
ignore = false;
|
ignore = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
redos = [];
|
hist.re = [];
|
||||||
clearTimeout(sched_timer);
|
clearTimeout(sched_timer);
|
||||||
sched_cpos = dom_src.selectionEnd;
|
sched_cpos = dom_src.selectionEnd;
|
||||||
sched_timer = setTimeout(push, 500);
|
sched_timer = setTimeout(push, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
function undo() {
|
function undo() {
|
||||||
if (redos.length == 0) {
|
if (hist.re.length == 0) {
|
||||||
clearTimeout(sched_timer);
|
clearTimeout(sched_timer);
|
||||||
push();
|
push();
|
||||||
}
|
}
|
||||||
return apply(undos, redos);
|
return apply(hist.un, hist.re);
|
||||||
}
|
}
|
||||||
|
|
||||||
function redo() {
|
function redo() {
|
||||||
return apply(redos, undos);
|
return apply(hist.re, hist.un);
|
||||||
}
|
}
|
||||||
|
|
||||||
function push() {
|
function push() {
|
||||||
var newtxt = dom_src.value;
|
var newtxt = dom_src.value;
|
||||||
var change = diff(ref, newtxt, sched_cpos);
|
var change = diff(ref, newtxt, sched_cpos);
|
||||||
if (change !== null)
|
if (change !== null)
|
||||||
undos.push(change);
|
hist.un.push(change);
|
||||||
|
|
||||||
ref = newtxt;
|
ref = newtxt;
|
||||||
dbg('undos(%d) redos(%d)', undos.length, redos.length);
|
dbg('undos(%d) redos(%d)', hist.un.length, hist.re.length);
|
||||||
if (undos.length > 0)
|
if (hist.un.length > 0)
|
||||||
dbg(undos.slice(-1)[0]);
|
dbg(static(hist.un.slice(-1)[0]));
|
||||||
if (redos.length > 0)
|
if (hist.re.length > 0)
|
||||||
dbg(redos.slice(-1)[0]);
|
dbg(static(hist.re.slice(-1)[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -618,8 +677,7 @@ action_stack = (function () {
|
|||||||
undo: undo,
|
undo: undo,
|
||||||
redo: redo,
|
redo: redo,
|
||||||
push: schedule_push,
|
push: schedule_push,
|
||||||
_undos: undos,
|
_hist: hist,
|
||||||
_redos: redos,
|
|
||||||
_ref: ref
|
_ref: ref
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -80,3 +80,22 @@ for d in /usr /var; do find $d -type f -size +30M 2>/dev/null; done | while IFS=
|
|||||||
# py2 on osx
|
# py2 on osx
|
||||||
brew install python@2
|
brew install python@2
|
||||||
pip install virtualenv
|
pip install virtualenv
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
## http 206
|
||||||
|
|
||||||
|
# az = abcdefghijklmnopqrstuvwxyz
|
||||||
|
|
||||||
|
printf '%s\r\n' 'GET /az HTTP/1.1' 'Host: ocv.me' 'Range: bytes=5-10' '' | ncat ocv.me 80
|
||||||
|
# Content-Range: bytes 5-10/26
|
||||||
|
# Content-Length: 6
|
||||||
|
# fghijk
|
||||||
|
|
||||||
|
Range: bytes=0-1 "ab" Content-Range: bytes 0-1/26
|
||||||
|
Range: bytes=24-24 "y" Content-Range: bytes 24-24/26
|
||||||
|
Range: bytes=24-25 "yz" Content-Range: bytes 24-25/26
|
||||||
|
Range: bytes=24- "yz" Content-Range: bytes 24-25/26
|
||||||
|
Range: bytes=25-29 "z" Content-Range: bytes 25-25/26
|
||||||
|
Range: bytes=26- Content-Range: bytes */26
|
||||||
|
HTTP/1.1 416 Requested Range Not Satisfiable
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ diff --git a/src/Parser.js b/src/Parser.js
|
|||||||
+ // similar to tables, writing contents before the <ul> tag
|
+ // similar to tables, writing contents before the <ul> tag
|
||||||
+ // so update the tag attribute as we go
|
+ // so update the tag attribute as we go
|
||||||
+ // (assuming all list entries got tagged with a source-line, probably safe w)
|
+ // (assuming all list entries got tagged with a source-line, probably safe w)
|
||||||
+ body += this.renderer.tag_ln(item.tokens[0].ln).listitem(itemBody, task, checked);
|
+ body += this.renderer.tag_ln((item.tokens[0] || token).ln).listitem(itemBody, task, checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
- out += this.renderer.list(body, ordered, start);
|
- out += this.renderer.list(body, ordered, start);
|
||||||
|
|||||||
@@ -166,3 +166,6 @@ chmod 755 $sfx_out.*
|
|||||||
printf "done:\n"
|
printf "done:\n"
|
||||||
printf " %s\n" "$(realpath $sfx_out)."{sh,py}
|
printf " %s\n" "$(realpath $sfx_out)."{sh,py}
|
||||||
# rm -rf *
|
# rm -rf *
|
||||||
|
|
||||||
|
# tar -tvf ../sfx/tar | sed -r 's/(.* ....-..-.. ..:.. )(.*)/\2 `` \1/' | sort | sed -r 's/(.*) `` (.*)/\2 \1/'| less
|
||||||
|
# for n in {1..9}; do tar -tf tar | grep -vE '/$' | sed -r 's/(.*)\.(.*)/\2.\1/' | sort | sed -r 's/([^\.]+)\.(.*)/\2.\1/' | tar -cT- | bzip2 -c$n | wc -c; done
|
||||||
|
|||||||
Reference in New Issue
Block a user