Compare commits

...

7 Commits

Author SHA1 Message Date
ed
dcaf7b0a20 v1.1.5 2021-12-04 03:33:57 +01:00
ed
f982cdc178 spa gridview 2021-12-04 03:31:12 +01:00
ed
b265e59834 spa filetab 2021-12-04 03:25:28 +01:00
ed
4a843a6624 unflicker navpane + add client state escape hatch 2021-12-04 02:46:00 +01:00
ed
241ef5b99d preserve mtimes when juggling symlinks 2021-12-04 01:58:04 +01:00
ed
f39f575a9c sort-order indicators 2021-12-03 23:53:41 +01:00
ed
1521307f1e use preferred sort on initial render, fixes #8 2021-12-03 02:07:08 +01:00
13 changed files with 254 additions and 124 deletions

View File

@@ -13,7 +13,7 @@
upstream cpp { upstream cpp {
server 127.0.0.1:3923; server 127.0.0.1:3923;
keepalive 120; keepalive 1;
} }
server { server {
listen 443 ssl; listen 443 ssl;

View File

@@ -1,8 +1,8 @@
# coding: utf-8 # coding: utf-8
VERSION = (1, 1, 4) VERSION = (1, 1, 5)
CODENAME = "opus" CODENAME = "opus"
BUILD_DT = (2021, 11, 28) BUILD_DT = (2021, 12, 4)
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)

View File

@@ -2,7 +2,7 @@
from __future__ import print_function, unicode_literals from __future__ import print_function, unicode_literals
import os import os
from ..util import fsenc, fsdec from ..util import fsenc, fsdec, SYMTIME
from . import path from . import path
@@ -55,5 +55,8 @@ def unlink(p):
return os.unlink(fsenc(p)) return os.unlink(fsenc(p))
def utime(p, times=None): def utime(p, times=None, follow_symlinks=True):
return os.utime(fsenc(p), times) if SYMTIME:
return os.utime(fsenc(p), times, follow_symlinks=follow_symlinks)
else:
return os.utime(fsenc(p), times)

View File

@@ -2,7 +2,7 @@
from __future__ import print_function, unicode_literals from __future__ import print_function, unicode_literals
import os import os
from ..util import fsenc, fsdec from ..util import fsenc, fsdec, SYMTIME
def abspath(p): def abspath(p):
@@ -13,8 +13,11 @@ def exists(p):
return os.path.exists(fsenc(p)) return os.path.exists(fsenc(p))
def getmtime(p): def getmtime(p, follow_symlinks=True):
return os.path.getmtime(fsenc(p)) if not follow_symlinks and SYMTIME:
return os.lstat(fsenc(p)).st_mtime
else:
return os.path.getmtime(fsenc(p))
def getsize(p): def getsize(p):

View File

@@ -60,6 +60,7 @@ class HttpCli(object):
self.bufsz = 1024 * 32 self.bufsz = 1024 * 32
self.hint = None self.hint = None
self.trailing_slash = True self.trailing_slash = True
self.out_headerlist = []
self.out_headers = { self.out_headers = {
"Access-Control-Allow-Origin": "*", "Access-Control-Allow-Origin": "*",
"Cache-Control": "no-store; max-age=0", "Cache-Control": "no-store; max-age=0",
@@ -226,7 +227,7 @@ class HttpCli(object):
self.gvol = self.asrv.vfs.aget[self.uname] self.gvol = self.asrv.vfs.aget[self.uname]
if pwd and "pw" in self.ouparam and pwd != cookies.get("cppwd"): if pwd and "pw" in self.ouparam and pwd != cookies.get("cppwd"):
self.out_headers["Set-Cookie"] = self.get_pwd_cookie(pwd)[0] self.out_headerlist.append(("Set-Cookie", self.get_pwd_cookie(pwd)[0]))
self.ua = self.headers.get("user-agent", "") self.ua = self.headers.get("user-agent", "")
self.is_rclone = self.ua.startswith("rclone/") self.is_rclone = self.ua.startswith("rclone/")
@@ -285,7 +286,7 @@ class HttpCli(object):
self.out_headers["Cache-Control"] = "max-age=" + n self.out_headers["Cache-Control"] = "max-age=" + n
def k304(self): def k304(self):
k304 = self.cookies.get("k304", "") k304 = self.cookies.get("k304")
return k304 == "y" or ("; Trident/" in self.ua and not k304) return k304 == "y" or ("; Trident/" in self.ua and not k304)
def send_headers(self, length, status=200, mime=None, headers=None): def send_headers(self, length, status=200, mime=None, headers=None):
@@ -310,7 +311,7 @@ class HttpCli(object):
self.out_headers["Content-Type"] = mime self.out_headers["Content-Type"] = mime
for k, v in self.out_headers.items(): for k, v in list(self.out_headers.items()) + self.out_headerlist:
response.append("{}: {}".format(k, v)) response.append("{}: {}".format(k, v))
try: try:
@@ -439,6 +440,12 @@ class HttpCli(object):
if "k304" in self.uparam: if "k304" in self.uparam:
return self.set_k304() return self.set_k304()
if "am_js" in self.uparam:
return self.set_am_js()
if "reset" in self.uparam:
return self.set_cfg_reset()
if "h" in self.uparam: if "h" in self.uparam:
return self.tx_mounts() return self.tx_mounts()
@@ -1717,7 +1724,19 @@ class HttpCli(object):
def set_k304(self): def set_k304(self):
ck = gencookie("k304", self.uparam["k304"], 60 * 60 * 24 * 365) ck = gencookie("k304", self.uparam["k304"], 60 * 60 * 24 * 365)
self.out_headers["Set-Cookie"] = ck self.out_headerlist.append(("Set-Cookie", ck))
self.redirect("", "?h#cc")
def set_am_js(self):
v = "n" if self.uparam["am_js"] == "n" else "y"
ck = gencookie("js", v, 60 * 60 * 24 * 365)
self.out_headerlist.append(("Set-Cookie", ck))
self.reply(b"promoted\n")
def set_cfg_reset(self):
for k in ("k304", "js", "cppwd"):
self.out_headerlist.append(("Set-Cookie", gencookie(k, "x", None)))
self.redirect("", "?h#cc") self.redirect("", "?h#cc")
def tx_404(self, is_403=False): def tx_404(self, is_403=False):
@@ -2113,6 +2132,7 @@ class HttpCli(object):
"vdir": quotep(self.vpath), "vdir": quotep(self.vpath),
"vpnodes": vpnodes, "vpnodes": vpnodes,
"files": [], "files": [],
"ls0": None,
"acct": self.uname, "acct": self.uname,
"perms": json.dumps(perms), "perms": json.dumps(perms),
"taglist": [], "taglist": [],
@@ -2336,7 +2356,12 @@ class HttpCli(object):
dirs.sort(key=itemgetter("name")) dirs.sort(key=itemgetter("name"))
j2a["files"] = dirs + files if self.cookies.get("js") == "y":
j2a["ls0"] = {"dirs": dirs, "files": files, "taglist": taglist}
j2a["files"] = []
else:
j2a["files"] = dirs + files
j2a["logues"] = logues j2a["logues"] = logues
j2a["taglist"] = taglist j2a["taglist"] = taglist
j2a["txt_ext"] = self.args.textfiles.replace(",", " ") j2a["txt_ext"] = self.args.textfiles.replace(",", " ")

View File

@@ -21,6 +21,7 @@ from .util import (
Pebkac, Pebkac,
Queue, Queue,
ProgressPrinter, ProgressPrinter,
SYMTIME,
fsdec, fsdec,
fsenc, fsenc,
absreal, absreal,
@@ -1307,7 +1308,7 @@ class Up2k(object):
err = "partial upload exists at a different location; please resume uploading here instead:\n" err = "partial upload exists at a different location; please resume uploading here instead:\n"
err += "/" + quotep(vsrc) + " " err += "/" + quotep(vsrc) + " "
dupe = [cj["prel"], cj["name"]] dupe = [cj["prel"], cj["name"], cj["lmod"]]
try: try:
self.dupesched[src].append(dupe) self.dupesched[src].append(dupe)
except: except:
@@ -1332,7 +1333,7 @@ class Up2k(object):
dst = os.path.join(job["ptop"], job["prel"], job["name"]) dst = os.path.join(job["ptop"], job["prel"], job["name"])
if not self.args.nw: if not self.args.nw:
bos.unlink(dst) # TODO ed pls bos.unlink(dst) # TODO ed pls
self._symlink(src, dst) self._symlink(src, dst, lmod=cj["lmod"])
if cur: if cur:
a = [cj[x] for x in "prel name lmod size addr".split()] a = [cj[x] for x in "prel name lmod size addr".split()]
@@ -1404,13 +1405,14 @@ class Up2k(object):
with ren_open(fname, "wb", fdir=fdir, suffix=suffix) as f: with ren_open(fname, "wb", fdir=fdir, suffix=suffix) as f:
return f["orz"][1] return f["orz"][1]
def _symlink(self, src, dst, verbose=True): def _symlink(self, src, dst, verbose=True, lmod=None):
if verbose: if verbose:
self.log("linking dupe:\n {0}\n {1}".format(src, dst)) self.log("linking dupe:\n {0}\n {1}".format(src, dst))
if self.args.nw: if self.args.nw:
return return
linked = False
try: try:
if self.args.no_symlink: if self.args.no_symlink:
raise Exception("disabled in config") raise Exception("disabled in config")
@@ -1441,10 +1443,18 @@ class Up2k(object):
hops = len(ndst[nc:]) - 1 hops = len(ndst[nc:]) - 1
lsrc = "../" * hops + "/".join(lsrc) lsrc = "../" * hops + "/".join(lsrc)
os.symlink(fsenc(lsrc), fsenc(ldst)) os.symlink(fsenc(lsrc), fsenc(ldst))
linked = True
except Exception as ex: except Exception as ex:
self.log("cannot symlink; creating copy: " + repr(ex)) self.log("cannot symlink; creating copy: " + repr(ex))
shutil.copy2(fsenc(src), fsenc(dst)) shutil.copy2(fsenc(src), fsenc(dst))
if lmod and (not linked or SYMTIME):
times = (int(time.time()), int(lmod))
if ANYWIN:
self.lastmod_q.put([dst, 0, times])
else:
bos.utime(dst, times, False)
def handle_chunk(self, ptop, wark, chash): def handle_chunk(self, ptop, wark, chash):
with self.mutex: with self.mutex:
job = self.registry[ptop].get(wark) job = self.registry[ptop].get(wark)
@@ -1551,12 +1561,12 @@ class Up2k(object):
return return
cur = self.cur.get(ptop) cur = self.cur.get(ptop)
for rd, fn in dupes: for rd, fn, lmod in dupes:
d2 = os.path.join(ptop, rd, fn) d2 = os.path.join(ptop, rd, fn)
if os.path.exists(d2): if os.path.exists(d2):
continue continue
self._symlink(dst, d2) self._symlink(dst, d2, lmod=lmod)
if cur: if cur:
self.db_rm(cur, rd, fn) self.db_rm(cur, rd, fn)
self.db_add(cur, wark, rd, fn, *a[-4:]) self.db_add(cur, wark, rd, fn, *a[-4:])
@@ -1773,8 +1783,9 @@ class Up2k(object):
dlabs = absreal(sabs) dlabs = absreal(sabs)
m = "moving symlink from [{}] to [{}], target [{}]" m = "moving symlink from [{}] to [{}], target [{}]"
self.log(m.format(sabs, dabs, dlabs)) self.log(m.format(sabs, dabs, dlabs))
os.unlink(sabs) mt = bos.path.getmtime(sabs, False)
self._symlink(dlabs, dabs, False) bos.unlink(sabs)
self._symlink(dlabs, dabs, False, lmod=mt)
# folders are too scary, schedule rescan of both vols # folders are too scary, schedule rescan of both vols
self.need_rescan[svn.vpath] = 1 self.need_rescan[svn.vpath] = 1
@@ -1904,25 +1915,30 @@ class Up2k(object):
slabs = list(sorted(links.keys()))[0] slabs = list(sorted(links.keys()))[0]
ptop, rem = links.pop(slabs) ptop, rem = links.pop(slabs)
self.log("linkswap [{}] and [{}]".format(sabs, slabs)) self.log("linkswap [{}] and [{}]".format(sabs, slabs))
mt = bos.path.getmtime(slabs, False)
bos.unlink(slabs) bos.unlink(slabs)
bos.rename(sabs, slabs) bos.rename(sabs, slabs)
bos.utime(slabs, (int(time.time()), int(mt)), False)
self._symlink(slabs, sabs, False) self._symlink(slabs, sabs, False)
full[slabs] = [ptop, rem] full[slabs] = [ptop, rem]
sabs = slabs
if not dabs: if not dabs:
dabs = list(sorted(full.keys()))[0] dabs = list(sorted(full.keys()))[0]
for alink in links.keys(): for alink in links.keys():
lmod = None
try: try:
if alink != sabs and absreal(alink) != sabs: if alink != sabs and absreal(alink) != sabs:
continue continue
self.log("relinking [{}] to [{}]".format(alink, dabs)) self.log("relinking [{}] to [{}]".format(alink, dabs))
lmod = bos.path.getmtime(alink, False)
bos.unlink(alink) bos.unlink(alink)
except: except:
pass pass
self._symlink(dabs, alink, False) self._symlink(dabs, alink, False, lmod=lmod)
return len(full) + len(links) return len(full) + len(links)
@@ -2028,7 +2044,7 @@ class Up2k(object):
for path, sz, times in ready: for path, sz, times in ready:
self.log("lmod: setting times {} on {}".format(times, path)) self.log("lmod: setting times {} on {}".format(times, path))
try: try:
bos.utime(path, times) bos.utime(path, times, False)
except: except:
self.log("lmod: failed to utime ({}, {})".format(path, times)) self.log("lmod: failed to utime ({}, {})".format(path, times))

View File

@@ -67,8 +67,9 @@ if WINDOWS and PY2:
FS_ENCODING = "utf-8" FS_ENCODING = "utf-8"
HTTP_TS_FMT = "%a, %d %b %Y %H:%M:%S GMT" SYMTIME = sys.version_info >= (3, 6) and os.supports_follow_symlinks
HTTP_TS_FMT = "%a, %d %b %Y %H:%M:%S GMT"
HTTPCODE = { HTTPCODE = {
200: "OK", 200: "OK",

View File

@@ -79,6 +79,27 @@ a, #files tbody div a:last-child {
color: #999; color: #999;
font-weight: normal; font-weight: normal;
} }
.s0:after,
.s1:after {
content: '⌄';
margin-left: -.1em;
}
.s0r:after,
.s1r:after {
content: '⌃';
margin-left: -.1em;
}
.s0:after,
.s0r:after {
color: #fb0;
}
.s1:after,
.s1r:after {
color: #d09;
}
#files thead th:after {
margin-right: -.7em;
}
#files tbody tr:hover td { #files tbody tr:hover td {
background: #1c1c1c; background: #1c1c1c;
} }
@@ -1042,7 +1063,6 @@ html.light #rui {
font-size: 1.5em; font-size: 1.5em;
} }
#doc { #doc {
background: none;
overflow: visible; overflow: visible;
margin: -1em 0 .5em 0; margin: -1em 0 .5em 0;
padding: 1em 0 1em 0; padding: 1em 0 1em 0;
@@ -1130,6 +1150,7 @@ a.btn,
html, html,
#doc,
#rui, #rui,
#files td, #files td,
#files thead th, #files thead th,
@@ -1211,6 +1232,7 @@ html.light {
html.light #ops, html.light #ops,
html.light .opbox, html.light .opbox,
html.light #path, html.light #path,
html.light #doc,
html.light #srch_form, html.light #srch_form,
html.light .ghead, html.light .ghead,
html.light #u2etas { html.light #u2etas {
@@ -1288,6 +1310,14 @@ html.light #ops a,
html.light #files tbody div a:last-child { html.light #files tbody div a:last-child {
color: #06a; color: #06a;
} }
html.light .s0:after,
html.light .s0r:after {
color: #059;
}
html.light .s1:after,
html.light .s1r:after {
color: #f5d;
}
html.light #files thead th { html.light #files thead th {
background: #eaeaea; background: #eaeaea;
border-color: #ccc; border-color: #ccc;

View File

@@ -143,7 +143,8 @@
have_zip = {{ have_zip|tojson }}, have_zip = {{ have_zip|tojson }},
txt_ext = "{{ txt_ext }}", txt_ext = "{{ txt_ext }}",
{% if no_prism %}no_prism = 1,{% endif %} {% if no_prism %}no_prism = 1,{% endif %}
readme = {{ readme|tojson }}; readme = {{ readme|tojson }},
ls0 = {{ ls0|tojson }};
document.documentElement.setAttribute("class", localStorage.lightmode == 1 ? "light" : "dark"); document.documentElement.setAttribute("class", localStorage.lightmode == 1 ? "light" : "dark");
</script> </script>

View File

@@ -157,6 +157,7 @@ ebi('op_cfg').innerHTML = (
' <a id="thumbs" class="tgl btn" href="#" tt="in icon view, toggle icons or thumbnails$NHotkey: T">🖼️ thumbs</a>\n' + ' <a id="thumbs" class="tgl btn" href="#" tt="in icon view, toggle icons or thumbnails$NHotkey: T">🖼️ thumbs</a>\n' +
' <a id="dotfiles" class="tgl btn" href="#" tt="show hidden files (if server permits)">dotfiles</a>\n' + ' <a id="dotfiles" class="tgl btn" href="#" tt="show hidden files (if server permits)">dotfiles</a>\n' +
' <a id="ireadme" class="tgl btn" href="#" tt="show README.md in folder listings">📜 readme</a>\n' + ' <a id="ireadme" class="tgl btn" href="#" tt="show README.md in folder listings">📜 readme</a>\n' +
' <a id="spafiles" class="tgl btn" href="#" tt="speedboost when not using the navpane;$Nturn it off if things arent loading somehow">spa</a>\n' +
' </div>\n' + ' </div>\n' +
'</div>\n' + '</div>\n' +
(have_zip ? ( (have_zip ? (
@@ -306,7 +307,9 @@ function set_files_html(html) {
var ACtx = window.AudioContext || window.webkitAudioContext, var ACtx = window.AudioContext || window.webkitAudioContext,
actx = ACtx && new ACtx(); actx = ACtx && new ACtx(),
hash0 = location.hash,
mp;
var mpl = (function () { var mpl = (function () {
@@ -652,14 +655,11 @@ function MPlayer() {
}; };
} }
addcrc();
var mp = new MPlayer();
makeSortable(ebi('files'), mp.read_order.bind(mp));
function ft2dict(tr) { function ft2dict(tr) {
var th = ebi('files').tHead.rows[0].cells, var th = ebi('files').tHead.rows[0].cells,
rv = [], rv = [],
rh = [],
ra = [], ra = [],
rt = {}; rt = {};
@@ -671,10 +671,11 @@ function ft2dict(tr) {
if (!tv) if (!tv)
continue; continue;
(vis ? rv : ra).push(tk); (vis ? rv : rh).push(tk);
ra.push(tk);
rt[tk] = tv; rt[tk] = tv;
} }
return [rt, rv, ra]; return [rt, rv, rh, ra];
} }
@@ -808,7 +809,7 @@ var pbar = (function () {
bctx.clearRect(0, 0, bc.w, bc.h); bctx.clearRect(0, 0, bc.w, bc.h);
if (!mp.au) if (!mp || !mp.au)
return; return;
var sm = bc.w * 1.0 / mp.au.duration, var sm = bc.w * 1.0 / mp.au.duration,
@@ -835,7 +836,7 @@ var pbar = (function () {
pctx.clearRect(0, 0, pc.w, pc.h); pctx.clearRect(0, 0, pc.w, pc.h);
if (!mp.au || isNaN(adur = mp.au.duration) || isNaN(apos = mp.au.currentTime) || apos < 0 || adur < apos) if (!mp || !mp.au || isNaN(adur = mp.au.duration) || isNaN(apos = mp.au.currentTime) || apos < 0 || adur < apos)
return; // not-init || unsupp-codec return; // not-init || unsupp-codec
var sm = bc.w * 1.0 / adur; var sm = bc.w * 1.0 / adur;
@@ -906,6 +907,9 @@ var vbar = (function () {
} }
r.draw = function () { r.draw = function () {
if (!mp)
return;
var gh = h + '' + light; var gh = h + '' + light;
if (gradh != gh) { if (gradh != gh) {
gradh = gh; gradh = gh;
@@ -1132,7 +1136,6 @@ var mpui = (function () {
if (mp.au.paused) if (mp.au.paused)
timer.rm(updater_impl); timer.rm(updater_impl);
} }
r.progress_updater();
return r; return r;
})(); })();
@@ -1492,7 +1495,8 @@ function play(tid, is_ev, seek, call_depth) {
} }
mpui.progress_updater(); mpui.progress_updater();
pbar.drawbuf(); pbar.onresize();
vbar.onresize();
mpl.announce(); mpl.announce();
return true; return true;
} }
@@ -1567,7 +1571,8 @@ function autoplay_blocked(seek) {
function eval_hash() { function eval_hash() {
var v = location.hash; var v = hash0;
hash0 = null;
if (!v) if (!v)
return; return;
@@ -2559,9 +2564,9 @@ var thegrid = (function () {
'<a href="#" class="btn" z="1.2" tt="Hotkey: shift-D">+</a></span> <span>chop: ' + '<a href="#" class="btn" z="1.2" tt="Hotkey: shift-D">+</a></span> <span>chop: ' +
'<a href="#" class="btn" l="-1" tt="truncate filenames more (show less)">&ndash;</a> ' + '<a href="#" class="btn" l="-1" tt="truncate filenames more (show less)">&ndash;</a> ' +
'<a href="#" class="btn" l="1" tt="truncate filenames less (show more)">+</a></span> <span>sort by: ' + '<a href="#" class="btn" l="1" tt="truncate filenames less (show more)">+</a></span> <span>sort by: ' +
'<a href="#" s="href">name</a>, ' + '<a href="#" s="href">name</a> ' +
'<a href="#" s="sz">size</a>, ' + '<a href="#" s="sz">size</a> ' +
'<a href="#" s="ts">date</a>, ' + '<a href="#" s="ts">date</a> ' +
'<a href="#" s="ext">type</a>' + '<a href="#" s="ext">type</a>' +
'</span></div>' + '</span></div>' +
'<div id="ggrid"></div>' '<div id="ggrid"></div>'
@@ -2675,14 +2680,12 @@ var thegrid = (function () {
href = noq_href(this), href = noq_href(this),
aplay = ebi('a' + oth.getAttribute('id')), aplay = ebi('a' + oth.getAttribute('id')),
is_img = /\.(gif|jpe?g|png|webp|webm|mp4)(\?|$)/i.test(href), is_img = /\.(gif|jpe?g|png|webp|webm|mp4)(\?|$)/i.test(href),
in_tree = null, is_dir = href.endsWith('/'),
in_tree = is_dir && treectl.find(oth.textContent.slice(0, -1)),
have_sel = QS('#files tr.sel'), have_sel = QS('#files tr.sel'),
td = oth.closest('td').nextSibling, td = oth.closest('td').nextSibling,
tr = td.parentNode; tr = td.parentNode;
if (href.endsWith('/'))
in_tree = treectl.find(oth.textContent.slice(0, -1));
if (r.sel && !dbl) { if (r.sel && !dbl) {
td.click(); td.click();
clmod(this, 'sel', clgot(tr, 'sel')); clmod(this, 'sel', clgot(tr, 'sel'));
@@ -2693,6 +2696,9 @@ var thegrid = (function () {
else if (in_tree && !have_sel) else if (in_tree && !have_sel)
in_tree.click(); in_tree.click();
else if (is_dir && !have_sel && treectl.spa)
treectl.reqls(href, true, true);
else if (!is_img && have_sel) else if (!is_img && have_sel)
window.open(href, '_blank'); window.open(href, '_blank');
@@ -2867,10 +2873,6 @@ var thegrid = (function () {
import_js('/.cpr/baguettebox.js', r.bagit); import_js('/.cpr/baguettebox.js', r.bagit);
}, 1); }, 1);
if (r.en) {
loadgrid();
}
return r; return r;
})(); })();
@@ -3434,6 +3436,7 @@ var treectl = (function () {
mentered = null, mentered = null,
treesz = clamp(icfg_get('treesz', 16), 10, 50); treesz = clamp(icfg_get('treesz', 16), 10, 50);
bcfg_bind(r, 'spa', 'spafiles', true);
bcfg_bind(r, 'ireadme', 'ireadme', true); bcfg_bind(r, 'ireadme', 'ireadme', true);
bcfg_bind(r, 'dyn', 'dyntree', true, onresize); bcfg_bind(r, 'dyn', 'dyntree', true, onresize);
bcfg_bind(r, 'dots', 'dotfiles', false, function (v) { bcfg_bind(r, 'dots', 'dotfiles', false, function (v) {
@@ -3614,6 +3617,7 @@ var treectl = (function () {
if (!QS(q)) if (!QS(q))
break; break;
} }
nq = Math.max(nq, get_evpath().split('/').length - 2);
var iw = (treesz + Math.max(0, nq)), var iw = (treesz + Math.max(0, nq)),
w = iw + 'em', w = iw + 'em',
w2 = (iw + 2) + 'em'; w2 = (iw + 2) + 'em';
@@ -3636,7 +3640,7 @@ var treectl = (function () {
r.goto = function (url, push) { r.goto = function (url, push) {
get_tree("", url, true); get_tree("", url, true);
reqls(url, push, true); r.reqls(url, push, true);
}; };
function get_tree(top, dst, rst) { function get_tree(top, dst, rst) {
@@ -3805,12 +3809,12 @@ var treectl = (function () {
treegrow.call(this.previousSibling, e); treegrow.call(this.previousSibling, e);
return; return;
} }
reqls(this.getAttribute('href'), true); r.reqls(this.getAttribute('href'), true);
r.dir_cb = tree_scrollto; r.dir_cb = tree_scrollto;
thegrid.setvis(true); thegrid.setvis(true);
} }
function reqls(url, hpush, no_tree) { r.reqls = function (url, hpush, no_tree) {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.top = url; xhr.top = url;
xhr.hpush = hpush; xhr.hpush = hpush;
@@ -3865,8 +3869,34 @@ var treectl = (function () {
ebi('srv_info').innerHTML = '<span>' + res.srvinf + '</span>'; ebi('srv_info').innerHTML = '<span>' + res.srvinf + '</span>';
var top = this.top, if (this.hpush && !showfile.active())
nodes = res.dirs.concat(res.files), hist_push(this.top);
r.gentab(this.top, res);
acct = res.acct;
apply_perms(res.perms);
despin('#files');
despin('#gfiles');
ebi('pro').innerHTML = res.logues ? res.logues[0] || "" : "";
ebi('epi').innerHTML = res.logues ? res.logues[1] || "" : "";
clmod(ebi('epi'), 'mdo');
if (res.readme)
show_readme(res.readme);
wintitle();
var fun = r.ls_cb;
if (fun) {
r.ls_cb = null;
fun();
}
eval_hash();
}
r.gentab = function (top, res) {
var nodes = res.dirs.concat(res.files),
html = mk_files_header(res.taglist), html = mk_files_header(res.taglist),
seen = {}; seen = {};
@@ -3882,7 +3912,7 @@ var treectl = (function () {
id = 'f-' + ('00000000' + crc32(fname)).slice(-8), id = 'f-' + ('00000000' + crc32(fname)).slice(-8),
lang = showfile.getlang(fname); lang = showfile.getlang(fname);
while (seen[id]) while (seen[id]) // ejyefs ev69gg y9j8sg .opus
id += 'a'; id += 'a';
seen[id] = 1; seen[id] = 1;
@@ -3914,36 +3944,33 @@ var treectl = (function () {
html = html.join('\n'); html = html.join('\n');
set_files_html(html); set_files_html(html);
if (this.hpush && !showfile.active())
hist_push(this.top);
acct = res.acct;
apply_perms(res.perms);
despin('#files');
despin('#gfiles');
ebi('pro').innerHTML = res.logues ? res.logues[0] || "" : "";
ebi('epi').innerHTML = res.logues ? res.logues[1] || "" : "";
clmod(ebi('epi'), 'mdo');
if (res.readme)
show_readme(res.readme);
wintitle();
filecols.set_style(); filecols.set_style();
showfile.mktree(); showfile.mktree();
mukey.render(); mukey.render();
reload_tree(); reload_tree();
reload_browser(); reload_browser();
tree_scrollto(); tree_scrollto();
var fun = r.ls_cb;
if (fun) {
r.ls_cb = null;
fun();
}
} }
r.hydrate = function () {
if (ls0 === null) {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/?am_js', true);
xhr.send();
return r.reqls(get_evpath(), false, true);
}
r.gentab(get_evpath(), ls0);
reload_browser();
pbar.onresize();
vbar.onresize();
mukey.render();
showfile.addlinks();
thegrid.setdirty();
setTimeout(eval_hash, 1);
};
function parsetree(res, top) { function parsetree(res, top) {
var ret = ''; var ret = '';
for (var a = 0; a < res.a.length; a++) { for (var a = 0; a < res.a.length; a++) {
@@ -4404,27 +4431,6 @@ var mukey = (function () {
})(); })();
function addcrc() {
var links = QSA(
'#files>tbody>tr>td:first-child+td>' + (
ebi('unsearch') ? 'div>a:last-child' : 'a'));
var seen = {}; // ejyefs ev69gg y9j8sg .opus
for (var a = 0, aa = links.length; a < aa; a++) {
var id = links[a].getAttribute('id');
if (!id) {
var crc = crc32(links[a].textContent || links[a].innerText);
id = 'f-' + ('00000000' + crc).slice(-8);
while (seen[id])
id += 'a';
links[a].setAttribute('id', id);
}
seen[id] = 1;
}
}
var light; var light;
(function () { (function () {
function freshen() { function freshen() {
@@ -4983,15 +4989,29 @@ function wintitle(txt) {
} }
ebi('path').onclick = function (e) {
var a = e.target.closest('a[href]');
if (!treectl.spa || !a || !(a = a.getAttribute('href') + '') || !a.endsWith('/'))
return;
treectl.reqls(a, true, true);
return ev(e);
};
ebi('files').onclick = ebi('docul').onclick = function (e) { ebi('files').onclick = ebi('docul').onclick = function (e) {
var tgt = e.target.closest('a[id]'); var tgt = e.target.closest('a[id]');
if (tgt && tgt.getAttribute('id').indexOf('f-') === 0 && tgt.textContent.endsWith('/')) { if (tgt && tgt.getAttribute('id').indexOf('f-') === 0 && tgt.textContent.endsWith('/')) {
var el = treectl.find(tgt.textContent.slice(0, -1)); var el = treectl.find(tgt.textContent.slice(0, -1));
if (!el) if (el) {
return; el.click();
return ev(e);
el.click(); }
return ev(e); if (treectl.spa) {
treectl.reqls(tgt.getAttribute('href'), true, true);
return ev(e);
}
return;
} }
tgt = e.target.closest('a[hl]'); tgt = e.target.closest('a[hl]');
@@ -4999,7 +5019,7 @@ ebi('files').onclick = ebi('docul').onclick = function (e) {
showfile.show(noq_href(ebi(tgt.getAttribute('hl'))), tgt.getAttribute('lang')); showfile.show(noq_href(ebi(tgt.getAttribute('hl'))), tgt.getAttribute('lang'));
return ev(e); return ev(e);
} }
} };
function reload_mp() { function reload_mp() {
@@ -5007,24 +5027,25 @@ function reload_mp() {
audio_eq.stop(); audio_eq.stop();
mp.au.pause(); mp.au.pause();
mp.au = null; mp.au = null;
mpl.unbuffer();
} }
mpl.stop(); mpl.stop();
var plays = QSA('tr>td:first-child>a.play'); var plays = QSA('tr>td:first-child>a.play');
for (var a = plays.length - 1; a >= 0; a--) for (var a = plays.length - 1; a >= 0; a--)
plays[a].parentNode.innerHTML = '-'; plays[a].parentNode.innerHTML = '-';
mpl.unbuffer();
mp = new MPlayer(); mp = new MPlayer();
audio_eq.acst = {}; audio_eq.acst = {};
setTimeout(pbar.onresize, 1); setTimeout(pbar.onresize, 1);
} }
function reload_browser(not_mp) { function reload_browser() {
filecols.set_style(); filecols.set_style();
var parts = get_evpath().split('/'), var parts = get_evpath().split('/'),
rm = QSA('#path>a+a+a'); rm = QSA('#path>a+a+a'),
ftab = ebi('files');
for (a = rm.length - 1; a >= 0; a--) for (a = rm.length - 1; a >= 0; a--)
rm[a].parentNode.removeChild(rm[a]); rm[a].parentNode.removeChild(rm[a]);
@@ -5046,11 +5067,9 @@ function reload_browser(not_mp) {
oo[a].textContent = hsz; oo[a].textContent = hsz;
} }
if (!not_mp) { reload_mp();
addcrc(); try { showsort(ftab); } catch (ex) { }
reload_mp(); makeSortable(ftab, mp.read_order.bind(mp));
makeSortable(ebi('files'), mp.read_order.bind(mp));
}
for (var a = 0; a < 2; a++) for (var a = 0; a < 2; a++)
clmod(ebi(a ? 'pro' : 'epi'), 'hidden', ebi('unsearch')); clmod(ebi(a ? 'pro' : 'epi'), 'hidden', ebi('unsearch'));
@@ -5061,7 +5080,4 @@ function reload_browser(not_mp) {
thegrid.setdirty(); thegrid.setdirty();
msel.render(); msel.render();
} }
reload_browser(true); treectl.hydrate();
showfile.addlinks();
mukey.render();
setTimeout(eval_hash, 1);

View File

@@ -81,9 +81,10 @@ table {
text-align: right; text-align: right;
} }
blockquote { blockquote {
margin: 0 0 0 .6em; margin: 0 0 1.6em .6em;
padding: .7em 1em; padding: .7em 1em 0 1em;
border-left: .3em solid rgba(128,128,128,0.5); border-left: .3em solid rgba(128,128,128,0.5);
border-radius: 0 0 0 .25em;
} }

View File

@@ -75,11 +75,13 @@
<h1 id="cc">client config:</h1> <h1 id="cc">client config:</h1>
<ul> <ul>
{% if k304 %} {% if k304 %}
<li><a href="/?k304=n" class="r">disable k304</a> (currently enabled) <li><a href="/?k304=n">disable k304</a> (currently enabled)
{%- else %} {%- else %}
<li><a href="/?k304=y">enable k304</a> (currently disabled) <li><a href="/?k304=y" class="r">enable k304</a> (currently disabled)
{% endif %} {% endif %}
<blockquote>enabling this will disconnect your client on every HTTP 304, which can prevent some buggy browsers/proxies from getting stuck (suddenly not being able to load pages), <em>but</em> it will also make things slower in general</blockquote></li> <blockquote>enabling this will disconnect your client on every HTTP 304, which can prevent some buggy browsers/proxies from getting stuck (suddenly not being able to load pages), <em>but</em> it will also make things slower in general</blockquote></li>
<li><a href="/?reset" class="r" onclick="localStorage.clear();return true">reset client settings</a></li>
</ul> </ul>
<h1>login for more:</h1> <h1>login for more:</h1>

View File

@@ -329,14 +329,45 @@ function clgot(el, cls) {
} }
function showsort(tab) {
var v, vn, v1, v2, th = tab.tHead,
sopts = jread('fsort', [["href", 1, ""]]);
th && (th = th.rows[0]) && (th = th.cells);
for (var a = sopts.length - 1; a >= 0; a--) {
if (!sopts[a][0])
continue;
v2 = v1;
v1 = sopts[a];
}
v = [v1, v2];
vn = [v1 ? v1[0] : '', v2 ? v2[0] : ''];
var ga = QSA('#ghead a[s]');
for (var a = 0; a < ga.length; a++)
ga[a].className = '';
for (var a = 0; a < th.length; a++) {
var n = vn.indexOf(th[a].getAttribute('name')),
cl = n < 0 ? ' ' : ' s' + n + (v[n][1] > 0 ? ' ' : 'r ');
th[a].className = th[a].className.replace(/ *s[01]r? */, ' ') + cl;
if (n + 1) {
ga = QS('#ghead a[s="' + vn[n] + '"]');
if (ga)
ga.className = cl;
}
}
}
function sortTable(table, col, cb) { function sortTable(table, col, cb) {
var tb = table.tBodies[0], var tb = table.tBodies[0],
th = table.tHead.rows[0].cells, th = table.tHead.rows[0].cells,
tr = Array.prototype.slice.call(tb.rows, 0), tr = Array.prototype.slice.call(tb.rows, 0),
i, reverse = th[col].className.indexOf('sort1') !== -1 ? -1 : 1; i, reverse = /s0[^r]/.exec(th[col].className + ' ') ? -1 : 1;
for (var a = 0, thl = th.length; a < thl; a++)
th[a].className = th[a].className.replace(/ *sort-?1 */, " ");
th[col].className += ' sort' + reverse;
var stype = th[col].getAttribute('sort'); var stype = th[col].getAttribute('sort');
try { try {
var nrules = [], rules = jread("fsort", []); var nrules = [], rules = jread("fsort", []);
@@ -354,6 +385,7 @@ function sortTable(table, col, cb) {
break; break;
} }
jwrite("fsort", nrules); jwrite("fsort", nrules);
try { showsort(table); } catch (ex) { }
} }
catch (ex) { catch (ex) {
console.log("failed to persist sort rules, resetting: " + ex); console.log("failed to persist sort rules, resetting: " + ex);