Compare commits

...

25 Commits

Author SHA1 Message Date
ed
1111baacb2 v1.3.0 2022-05-22 17:02:38 +02:00
ed
1b9c913efb update deps (marked, codemirror, prism) 2022-05-22 16:49:18 +02:00
ed
3524c36e1b tl 2022-05-22 16:04:10 +02:00
ed
cf87cea9f8 ux, tl 2022-05-21 11:32:25 +02:00
ed
bfa34404b8 ux tweaks 2022-05-19 18:00:33 +02:00
ed
0aba5f35bf add confirms on colhide, bigtxt 2022-05-19 17:59:33 +02:00
ed
663bc0842a ux 2022-05-18 19:51:25 +02:00
ed
7d10c96e73 grammar 2022-05-18 19:33:20 +02:00
ed
6b2720fab0 dont switch to treeview on play into next folder 2022-05-18 19:24:47 +02:00
ed
e74ad5132a persist videoplayer prefs 2022-05-18 19:17:21 +02:00
ed
1f6f89c1fd apply default-language to splashpage 2022-05-18 19:02:36 +02:00
ed
4d55e60980 update flat-light ss 2022-05-16 19:01:32 +02:00
ed
ddaaccd5af ux tweaks 2022-05-16 18:56:53 +02:00
ed
c20b7dac3d ah whatever, still 16 years left 2022-05-15 17:23:52 +02:00
ed
1f779d5094 zip: add ntfs and unix extensions for utc time 2022-05-15 16:13:49 +02:00
ed
715401ca8e fix timezone in search, zipfiles, fuse 2022-05-15 13:51:44 +02:00
ed
e7cd922d8b translate splashpage and search too 2022-05-15 13:20:52 +02:00
ed
187feee0c1 add norwegian translation 2022-05-14 23:25:40 +02:00
ed
49e962a7dc dbtool: faster, add examples,
match on hashes rather than paths by default,
add no-clobber option to keep existing tags
2022-05-14 12:44:05 +02:00
ed
633ff601e5 perf + ux 2022-05-14 00:13:06 +02:00
ed
331cf37054 show loading progress for huge documents 2022-05-13 23:02:20 +02:00
ed
23e4b9002f support ?doc=mojibake 2022-05-13 18:10:55 +02:00
ed
c0de3c8053 v1.2.11 2022-05-13 17:24:50 +02:00
ed
a82a3b084a make search results unselectable 2022-05-13 17:18:19 +02:00
ed
67c298e66b don't embed huge docs (defer to ajax), closes #9 2022-05-13 17:08:17 +02:00
29 changed files with 1539 additions and 694 deletions

View File

@@ -814,10 +814,10 @@ also, `--force-js` disables the plain HTML folder listing, making things harder
you can change the default theme with `--theme 2`, and add your own themes by modifying `browser.css` or providing your own css to `--css-browser`, then telling copyparty they exist by increasing `--themes` you can change the default theme with `--theme 2`, and add your own themes by modifying `browser.css` or providing your own css to `--css-browser`, then telling copyparty they exist by increasing `--themes`
<table><tr><td width="33%" align="center"><a href="https://user-images.githubusercontent.com/241032/165864907-17e2ac7d-319d-4f25-8718-2f376f614b51.png"><img src="https://user-images.githubusercontent.com/241032/165867551-fceb35dd-38f0-42bb-bef3-25ba651ca69b.png"></a> <table><tr><td width="33%" align="center"><a href="https://user-images.githubusercontent.com/241032/165864907-17e2ac7d-319d-4f25-8718-2f376f614b51.png"><img src="https://user-images.githubusercontent.com/241032/165867551-fceb35dd-38f0-42bb-bef3-25ba651ca69b.png"></a>
0. classic dark</td><td width="33%" align="center"><a href="https://user-images.githubusercontent.com/241032/165864904-c5b67ddd-f383-4b9e-9f5a-a3bde183d256.png"><img src="https://user-images.githubusercontent.com/241032/165867556-077b6068-2488-4fae-bf88-1fce40e719bc.png"></a> 0. classic dark</td><td width="33%" align="center"><a href="https://user-images.githubusercontent.com/241032/168644399-68938de5-da9b-445f-8d92-b51c74b5f345.png"><img src="https://user-images.githubusercontent.com/241032/168644404-8e1a2fdc-6e59-4c41-905e-ba5399ed686f.png"></a>
2. flat dark</td><td width="33%" align="center"><a href="https://user-images.githubusercontent.com/241032/165864901-db13a429-a5da-496d-8bc6-ce838547f69d.png"><img src="https://user-images.githubusercontent.com/241032/165867560-aa834aef-58dc-4abe-baef-7e562b647945.png"></a> 2. flat pm-monokai</td><td width="33%" align="center"><a href="https://user-images.githubusercontent.com/241032/165864901-db13a429-a5da-496d-8bc6-ce838547f69d.png"><img src="https://user-images.githubusercontent.com/241032/165867560-aa834aef-58dc-4abe-baef-7e562b647945.png"></a>
4. vice</td></tr><tr><td align="center"><a href="https://user-images.githubusercontent.com/241032/165864905-692682eb-6fb4-4d40-b6fe-27d2c7d3e2a7.png"><img src="https://user-images.githubusercontent.com/241032/165867555-080b73b6-6d85-41bb-a7c6-ad277c608365.png"></a> 4. vice</td></tr><tr><td align="center"><a href="https://user-images.githubusercontent.com/241032/165864905-692682eb-6fb4-4d40-b6fe-27d2c7d3e2a7.png"><img src="https://user-images.githubusercontent.com/241032/165867555-080b73b6-6d85-41bb-a7c6-ad277c608365.png"></a>
1. classic light</td><td align="center"><a href="https://user-images.githubusercontent.com/241032/165864903-7fba1cb9-036b-4f11-90d5-28b7c0724353.png"><img src="https://user-images.githubusercontent.com/241032/165867557-b5cc0010-d880-48b1-8156-9c84f7bbc521.png"></a> 1. classic light</td><td align="center"><a href="https://user-images.githubusercontent.com/241032/168645276-fb02fd19-190a-407a-b8d3-d58fee277e02.png"><img src="https://user-images.githubusercontent.com/241032/168645280-f0662b3c-9764-4875-a2e2-d91cc8199b23.png"></a>
3. flat light 3. flat light
</td><td align="center"><a href="https://user-images.githubusercontent.com/241032/165864898-10ce7052-a117-4fcf-845b-b56c91687908.png"><img src="https://user-images.githubusercontent.com/241032/165867562-f3003d45-dd2a-4564-8aae-fed44c1ae064.png"></a> </td><td align="center"><a href="https://user-images.githubusercontent.com/241032/165864898-10ce7052-a117-4fcf-845b-b56c91687908.png"><img src="https://user-images.githubusercontent.com/241032/165867562-f3003d45-dd2a-4564-8aae-fed44c1ae064.png"></a>
5. <a href="https://blog.codinghorror.com/a-tribute-to-the-windows-31-hot-dog-stand-color-scheme/">hotdog stand</a></td></tr></table> 5. <a href="https://blog.codinghorror.com/a-tribute-to-the-windows-31-hot-dog-stand-color-scheme/">hotdog stand</a></td></tr></table>

View File

@@ -42,6 +42,7 @@ import threading
import traceback import traceback
import http.client # py2: httplib import http.client # py2: httplib
import urllib.parse import urllib.parse
import calendar
from datetime import datetime from datetime import datetime
from urllib.parse import quote_from_bytes as quote from urllib.parse import quote_from_bytes as quote
from urllib.parse import unquote_to_bytes as unquote from urllib.parse import unquote_to_bytes as unquote
@@ -495,7 +496,7 @@ class Gateway(object):
ts = 60 * 60 * 24 * 2 ts = 60 * 60 * 24 * 2
try: try:
sz = int(fsize) sz = int(fsize)
ts = datetime.strptime(fdate, "%Y-%m-%d %H:%M:%S").timestamp() ts = calendar.timegm(time.strptime(fdate, "%Y-%m-%d %H:%M:%S"))
except: except:
info("bad HTML or OS [{}] [{}]".format(fdate, fsize)) info("bad HTML or OS [{}] [{}]".format(fdate, fsize))
# python cannot strptime(1959-01-01) on windows # python cannot strptime(1959-01-01) on windows

View File

@@ -45,6 +45,7 @@ import threading
import traceback import traceback
import http.client # py2: httplib import http.client # py2: httplib
import urllib.parse import urllib.parse
import calendar
from datetime import datetime from datetime import datetime
from urllib.parse import quote_from_bytes as quote from urllib.parse import quote_from_bytes as quote
from urllib.parse import unquote_to_bytes as unquote from urllib.parse import unquote_to_bytes as unquote
@@ -443,7 +444,7 @@ class Gateway(object):
ts = 60 * 60 * 24 * 2 ts = 60 * 60 * 24 * 2
try: try:
sz = int(fsize) sz = int(fsize)
ts = datetime.strptime(fdate, "%Y-%m-%d %H:%M:%S").timestamp() ts = calendar.timegm(time.strptime(fdate, "%Y-%m-%d %H:%M:%S"))
except: except:
info("bad HTML or OS [{}] [{}]".format(fdate, fsize)) info("bad HTML or OS [{}] [{}]".format(fdate, fsize))
# python cannot strptime(1959-01-01) on windows # python cannot strptime(1959-01-01) on windows

View File

@@ -8,7 +8,10 @@ import sqlite3
import argparse import argparse
DB_VER1 = 3 DB_VER1 = 3
DB_VER2 = 4 DB_VER2 = 5
BY_PATH = None
NC = None
def die(msg): def die(msg):
@@ -57,8 +60,13 @@ def compare(n1, d1, n2, d2, verbose):
if rd.split("/", 1)[0] == ".hist": if rd.split("/", 1)[0] == ".hist":
continue continue
if BY_PATH:
q = "select w from up where rd = ? and fn = ?" q = "select w from up where rd = ? and fn = ?"
hit = d2.execute(q, (rd, fn)).fetchone() hit = d2.execute(q, (rd, fn)).fetchone()
else:
q = "select w from up where substr(w,1,16) = ? and +w = ?"
hit = d2.execute(q, (w1[:16], w1)).fetchone()
if not hit: if not hit:
miss += 1 miss += 1
if verbose: if verbose:
@@ -70,27 +78,32 @@ def compare(n1, d1, n2, d2, verbose):
n = 0 n = 0
miss = {} miss = {}
nmiss = 0 nmiss = 0
for w1, k, v in d1.execute("select * from mt"): for w1s, k, v in d1.execute("select * from mt"):
n += 1 n += 1
if n % 100_000 == 0: if n % 100_000 == 0:
m = f"\033[36mchecked {n:,} of {nt:,} tags in {n1} against {n2}, so far {nmiss} missing tags\033[0m" m = f"\033[36mchecked {n:,} of {nt:,} tags in {n1} against {n2}, so far {nmiss} missing tags\033[0m"
print(m) print(m)
q = "select rd, fn from up where substr(w,1,16) = ?" q = "select w, rd, fn from up where substr(w,1,16) = ?"
rd, fn = d1.execute(q, (w1,)).fetchone() w1, rd, fn = d1.execute(q, (w1s,)).fetchone()
if rd.split("/", 1)[0] == ".hist": if rd.split("/", 1)[0] == ".hist":
continue continue
q = "select substr(w,1,16) from up where rd = ? and fn = ?" if BY_PATH:
q = "select w from up where rd = ? and fn = ?"
w2 = d2.execute(q, (rd, fn)).fetchone() w2 = d2.execute(q, (rd, fn)).fetchone()
else:
q = "select w from up where substr(w,1,16) = ? and +w = ?"
w2 = d2.execute(q, (w1s, w1)).fetchone()
if w2: if w2:
w2 = w2[0] w2 = w2[0]
v2 = None v2 = None
if w2: if w2:
v2 = d2.execute( v2 = d2.execute(
"select v from mt where w = ? and +k = ?", (w2, k) "select v from mt where w = ? and +k = ?", (w2[:16], k)
).fetchone() ).fetchone()
if v2: if v2:
v2 = v2[0] v2 = v2[0]
@@ -124,7 +137,7 @@ def compare(n1, d1, n2, d2, verbose):
for k, v in sorted(miss.items()): for k, v in sorted(miss.items()):
if v: if v:
print(f"{n1} has {v:6} more {k:<6} tags than {n2}") print(f"{n1} has {v:7} more {k:<7} tags than {n2}")
print(f"in total, {nmiss} missing tags in {n2}\n") print(f"in total, {nmiss} missing tags in {n2}\n")
@@ -132,47 +145,75 @@ def compare(n1, d1, n2, d2, verbose):
def copy_mtp(d1, d2, tag, rm): def copy_mtp(d1, d2, tag, rm):
nt = next(d1.execute("select count(w) from mt where k = ?", (tag,)))[0] nt = next(d1.execute("select count(w) from mt where k = ?", (tag,)))[0]
n = 0 n = 0
ndone = 0 ncopy = 0
for w1, k, v in d1.execute("select * from mt where k = ?", (tag,)): nskip = 0
for w1s, k, v in d1.execute("select * from mt where k = ?", (tag,)):
n += 1 n += 1
if n % 25_000 == 0: if n % 25_000 == 0:
m = f"\033[36m{n:,} of {nt:,} tags checked, so far {ndone} copied\033[0m" m = f"\033[36m{n:,} of {nt:,} tags checked, so far {ncopy} copied, {nskip} skipped\033[0m"
print(m) print(m)
q = "select rd, fn from up where substr(w,1,16) = ?" q = "select w, rd, fn from up where substr(w,1,16) = ?"
rd, fn = d1.execute(q, (w1,)).fetchone() w1, rd, fn = d1.execute(q, (w1s,)).fetchone()
if rd.split("/", 1)[0] == ".hist": if rd.split("/", 1)[0] == ".hist":
continue continue
q = "select substr(w,1,16) from up where rd = ? and fn = ?" if BY_PATH:
q = "select w from up where rd = ? and fn = ?"
w2 = d2.execute(q, (rd, fn)).fetchone() w2 = d2.execute(q, (rd, fn)).fetchone()
else:
q = "select w from up where substr(w,1,16) = ? and +w = ?"
w2 = d2.execute(q, (w1s, w1)).fetchone()
if not w2: if not w2:
continue continue
w2 = w2[0] w2s = w2[0][:16]
hit = d2.execute("select v from mt where w = ? and +k = ?", (w2, k)).fetchone() hit = d2.execute("select v from mt where w = ? and +k = ?", (w2s, k)).fetchone()
if hit: if hit:
hit = hit[0] hit = hit[0]
if hit != v: if hit != v:
ndone += 1 if NC and hit is not None:
if hit is not None: nskip += 1
d2.execute("delete from mt where w = ? and +k = ?", (w2, k)) continue
d2.execute("insert into mt values (?,?,?)", (w2, k, v)) ncopy += 1
if hit is not None:
d2.execute("delete from mt where w = ? and +k = ?", (w2s, k))
d2.execute("insert into mt values (?,?,?)", (w2s, k, v))
if rm: if rm:
d2.execute("delete from mt where w = ? and +k = 't:mtp'", (w2,)) d2.execute("delete from mt where w = ? and +k = 't:mtp'", (w2s,))
d2.commit() d2.commit()
print(f"copied {ndone} {tag} tags over") print(f"copied {ncopy} {tag} tags over, skipped {nskip}")
def examples():
print(
"""
# clearing the journal
./dbtool.py up2k.db
# copy tags ".bpm" and "key" from old.db to up2k.db, and remove the mtp flag from matching files (so copyparty won't run any mtps on it)
./dbtool.py -ls up2k.db
./dbtool.py -src old.db up2k.db -cmp
./dbtool.py -src old.v3 up2k.db -rm-mtp-flag -copy key
./dbtool.py -src old.v3 up2k.db -rm-mtp-flag -copy .bpm -vac
"""
)
def main(): def main():
global NC, BY_PATH
os.system("") os.system("")
print() print()
ap = argparse.ArgumentParser() ap = argparse.ArgumentParser()
ap.add_argument("db", help="database to work on") ap.add_argument("db", help="database to work on")
ap.add_argument("-h2", action="store_true", help="show examples")
ap.add_argument("-src", metavar="DB", type=str, help="database to copy from") ap.add_argument("-src", metavar="DB", type=str, help="database to copy from")
ap2 = ap.add_argument_group("informational / read-only stuff") ap2 = ap.add_argument_group("informational / read-only stuff")
@@ -185,11 +226,29 @@ def main():
ap2.add_argument( ap2.add_argument(
"-rm-mtp-flag", "-rm-mtp-flag",
action="store_true", action="store_true",
help="when an mtp tag is copied over, also mark that as done, so copyparty won't run mtp on it", help="when an mtp tag is copied over, also mark that file as done, so copyparty won't run any mtps on those files",
) )
ap2.add_argument("-vac", action="store_true", help="optimize DB") ap2.add_argument("-vac", action="store_true", help="optimize DB")
ap2 = ap.add_argument_group("behavior modifiers")
ap2.add_argument(
"-nc",
action="store_true",
help="no-clobber; don't replace/overwrite existing tags",
)
ap2.add_argument(
"-by-path",
action="store_true",
help="match files based on location rather than warks (content-hash), use this if the databases have different wark salts",
)
ar = ap.parse_args() ar = ap.parse_args()
if ar.h2:
examples()
return
NC = ar.nc
BY_PATH = ar.by_path
for v in [ar.db, ar.src]: for v in [ar.db, ar.src]:
if v and not os.path.exists(v): if v and not os.path.exists(v):

View File

@@ -563,12 +563,14 @@ def run_argparse(argv, formatter):
ap2.add_argument("-mtp", metavar="M=[f,]BIN", type=u, action="append", help="read tag M using program BIN to parse the file") ap2.add_argument("-mtp", metavar="M=[f,]BIN", type=u, action="append", help="read tag M using program BIN to parse the file")
ap2 = ap.add_argument_group('ui options') ap2 = ap.add_argument_group('ui options')
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language")
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use") ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use")
ap2.add_argument("--themes", metavar="NUM", type=int, default=6, help="number of themes installed") ap2.add_argument("--themes", metavar="NUM", type=int, default=6, help="number of themes installed")
ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include") ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include")
ap2.add_argument("--css-browser", metavar="L", type=u, help="URL to additional CSS to include") ap2.add_argument("--css-browser", metavar="L", type=u, help="URL to additional CSS to include")
ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages") ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages")
ap2.add_argument("--textfiles", metavar="CSV", type=u, default="txt,nfo,diz,cue,readme", help="file extensions to present as plaintext") ap2.add_argument("--textfiles", metavar="CSV", type=u, default="txt,nfo,diz,cue,readme", help="file extensions to present as plaintext")
ap2.add_argument("--txt-max", metavar="KiB", type=int, default=64, help="max size of embedded textfiles on ?doc= (anything bigger will be lazy-loaded by JS)")
ap2.add_argument("--doctitle", metavar="TXT", type=u, default="copyparty", help="title / service-name to show in html documents") ap2.add_argument("--doctitle", metavar="TXT", type=u, default="copyparty", help="title / service-name to show in html documents")
ap2 = ap.add_argument_group('debug options') ap2 = ap.add_argument_group('debug options')

View File

@@ -1,8 +1,8 @@
# coding: utf-8 # coding: utf-8
VERSION = (1, 2, 10) VERSION = (1, 3, 0)
CODENAME = "ftp btw" CODENAME = "god dag"
BUILD_DT = (2022, 5, 13) BUILD_DT = (2022, 5, 22)
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

@@ -1829,12 +1829,12 @@ class HttpCli(object):
def tx_404(self, is_403=False): def tx_404(self, is_403=False):
rc = 404 rc = 404
if self.args.vague_403: if self.args.vague_403:
m = '<h1>404 not found &nbsp;┐( ´ -`)┌</h1><p>or maybe you don\'t have access -- try logging in or <a href="/?h">go home</a></p>' m = '<h1 id="n">404 not found &nbsp;┐( ´ -`)┌</h1><p id="o">or maybe you don\'t have access -- try logging in or <a href="/?h">go home</a></p>'
elif is_403: elif is_403:
m = '<h1>403 forbiddena &nbsp;~┻━┻</h1><p>you\'ll have to log in or <a href="/?h">go home</a></p>' m = '<h1 id="p">403 forbiddena &nbsp;~┻━┻</h1><p id="q">you\'ll have to log in or <a href="/?h">go home</a></p>'
rc = 403 rc = 403
else: else:
m = '<h1>404 not found &nbsp;┐( ´ -`)┌</h1><p><a href="/?h">go home</a></p>' m = '<h1 id="n">404 not found &nbsp;┐( ´ -`)┌</h1><p><a id="r" href="/?h">go home</a></p>'
html = self.j2("splash", this=self, qvpath=quotep(self.vpath), msg=m) html = self.j2("splash", this=self, qvpath=quotep(self.vpath), msg=m)
self.reply(html.encode("utf-8"), status=rc) self.reply(html.encode("utf-8"), status=rc)
@@ -2252,6 +2252,7 @@ class HttpCli(object):
"readme": readme, "readme": readme,
"title": html_escape(self.vpath, crlf=True), "title": html_escape(self.vpath, crlf=True),
"srv_info": srv_info, "srv_info": srv_info,
"lang": self.args.lang,
"dtheme": self.args.theme, "dtheme": self.args.theme,
"themes": self.args.themes, "themes": self.args.themes,
"turbolvl": self.args.turbo, "turbolvl": self.args.turbo,
@@ -2438,14 +2439,19 @@ class HttpCli(object):
if doc: if doc:
doc = unquotep(doc.replace("+", " ").split("?")[0]) doc = unquotep(doc.replace("+", " ").split("?")[0])
j2a["docname"] = doc j2a["docname"] = doc
doctxt = None
if next((x for x in files if x["name"] == doc), None): if next((x for x in files if x["name"] == doc), None):
with open(os.path.join(abspath, doc), "rb") as f: docpath = os.path.join(abspath, doc)
doc = f.read().decode("utf-8", "replace") sz = bos.path.getsize(docpath)
if sz < 1024 * self.args.txt_max:
with open(fsenc(docpath), "rb") as f:
doctxt = f.read().decode("utf-8", "replace")
else: else:
self.log("doc 404: [{}]".format(doc), c=6) self.log("doc 404: [{}]".format(doc), c=6)
doc = "( textfile not found )" doctxt = "( textfile not found )"
j2a["doc"] = doc if doctxt is not None:
j2a["doc"] = doctxt
if not self.conn.hsrv.prism: if not self.conn.hsrv.prism:
j2a["no_prism"] = True j2a["no_prism"] = True

View File

@@ -3,7 +3,7 @@ from __future__ import print_function, unicode_literals
import time import time
import zlib import zlib
from datetime import datetime import calendar
from .sutil import errdesc from .sutil import errdesc
from .util import yieldfile, sanitize_fn, spack, sunpack, min_ex from .util import yieldfile, sanitize_fn, spack, sunpack, min_ex
@@ -25,12 +25,12 @@ def dostime2unix(buf):
tf = "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}" tf = "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}"
iso = tf.format(*tt) iso = tf.format(*tt)
dt = datetime.strptime(iso, "%Y-%m-%d %H:%M:%S") dt = time.strptime(iso, "%Y-%m-%d %H:%M:%S")
return int(dt.timestamp()) return int(calendar.timegm(dt))
def unixtime2dos(ts): def unixtime2dos(ts):
tt = time.gmtime(ts) tt = time.gmtime(ts + 1)
dy, dm, dd, th, tm, ts = list(tt)[:6] dy, dm, dd, th, tm, ts = list(tt)[:6]
bd = ((dy - 1980) << 9) + (dm << 5) + dd bd = ((dy - 1980) << 9) + (dm << 5) + dd
@@ -76,7 +76,7 @@ def gen_hdr(h_pos, fn, sz, lastmod, utf8, crc32, pre_crc):
# 4b magic, 2b min-ver # 4b magic, 2b min-ver
ret = b"\x50\x4b\x03\x04" + req_ver ret = b"\x50\x4b\x03\x04" + req_ver
else: else:
# 4b magic, 2b spec-ver, 2b min-ver # 4b magic, 2b spec-ver (1b compat, 1b os (00 dos, 03 unix)), 2b min-ver
ret = b"\x50\x4b\x01\x02\x1e\x03" + req_ver ret = b"\x50\x4b\x01\x02\x1e\x03" + req_ver
ret += b"\x00" if pre_crc else b"\x08" # streaming ret += b"\x00" if pre_crc else b"\x08" # streaming
@@ -95,23 +95,34 @@ def gen_hdr(h_pos, fn, sz, lastmod, utf8, crc32, pre_crc):
fn = sanitize_fn(fn, "/", []) fn = sanitize_fn(fn, "/", [])
bfn = fn.encode("utf-8" if utf8 else "cp437", "replace").replace(b"?", b"_") bfn = fn.encode("utf-8" if utf8 else "cp437", "replace").replace(b"?", b"_")
# add ntfs (0x24) and/or unix (0x10) extrafields for utc, add z64 if requested
z64_len = len(z64v) * 8 + 4 if z64v else 0 z64_len = len(z64v) * 8 + 4 if z64v else 0
ret += spack(b"<HH", len(bfn), z64_len) ret += spack(b"<HH", len(bfn), 0x10 + z64_len)
if h_pos is not None: if h_pos is not None:
# 2b comment, 2b diskno # 2b comment, 2b diskno
ret += b"\x00" * 4 ret += b"\x00" * 4
# 2b internal.attr, 4b external.attr # 2b internal.attr, 4b external.attr
# infozip-macos: 0100 0000 a481 file:644 # infozip-macos: 0100 0000 a481 (spec-ver 1e03) file:644
# infozip-macos: 0100 0100 0080 file:000 # infozip-macos: 0100 0100 0080 (spec-ver 1e03) file:000
ret += b"\x01\x00\x00\x00\xa4\x81" # win10-zip: 0000 2000 0000 (spec-ver xx00) FILE_ATTRIBUTE_ARCHIVE
ret += b"\x00\x00\x00\x00\xa4\x81" # unx
# ret += b"\x00\x00\x20\x00\x00\x00" # fat
# 4b local-header-ofs # 4b local-header-ofs
ret += spack(b"<L", min(h_pos, 0xFFFFFFFF)) ret += spack(b"<L", min(h_pos, 0xFFFFFFFF))
ret += bfn ret += bfn
# ntfs: type 0a, size 20, rsvd, attr1, len 18, mtime, atime, ctime
# b"\xa3\x2f\x82\x41\x55\x68\xd8\x01" 1652616838.798941100 ~5.861518 132970904387989411 ~58615181
# nt = int((lastmod + 11644473600) * 10000000)
# ret += spack(b"<HHLHHQQQ", 0xA, 0x20, 0, 1, 0x18, nt, nt, nt)
# unix: type 0d, size 0c, atime, mtime, uid, gid
ret += spack(b"<HHLLHH", 0xD, 0xC, int(lastmod), int(lastmod), 1000, 1000)
if z64v: if z64v:
ret += spack(b"<HH" + b"Q" * len(z64v), 1, len(z64v) * 8, *z64v) ret += spack(b"<HH" + b"Q" * len(z64v), 1, len(z64v) * 8, *z64v)
@@ -205,7 +216,7 @@ class StreamZip(object):
st = f["st"] st = f["st"]
sz = st.st_size sz = st.st_size
ts = st.st_mtime + 1 ts = st.st_mtime
crc = None crc = None
if self.pre_crc: if self.pre_crc:

View File

@@ -4,8 +4,8 @@ from __future__ import print_function, unicode_literals
import re import re
import os import os
import time import time
import calendar
import threading import threading
from datetime import datetime
from operator import itemgetter from operator import itemgetter
from .__init__ import ANYWIN, unicode from .__init__ import ANYWIN, unicode
@@ -190,18 +190,17 @@ class U2idx(object):
if is_date: if is_date:
is_date = False is_date = False
v = v.upper().rstrip("Z").replace(",", " ").replace("T", " ") v = re.sub(r"[tzTZ, ]+", " ", v).strip()
while " " in v:
v = v.replace(" ", " ")
for fmt in [ for fmt in [
"%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M:%S",
"%Y-%m-%d %H:%M", "%Y-%m-%d %H:%M",
"%Y-%m-%d %H", "%Y-%m-%d %H",
"%Y-%m-%d", "%Y-%m-%d",
"%Y-%m",
"%Y",
]: ]:
try: try:
v = datetime.strptime(v, fmt).timestamp() v = calendar.timegm(time.strptime(v, fmt))
break break
except: except:
pass pass

View File

@@ -35,8 +35,8 @@ window.baguetteBox = (function () {
documentLastFocus = null, documentLastFocus = null,
isFullscreen = false, isFullscreen = false,
vmute = false, vmute = false,
vloop = false, vloop = sread('vmode') == 'L',
vnext = false, vnext = sread('vmode') == 'C',
resume_mp = false; resume_mp = false;
var onFSC = function (e) { var onFSC = function (e) {
@@ -320,6 +320,7 @@ window.baguetteBox = (function () {
btnVmode.setAttribute('aria-label', msg); btnVmode.setAttribute('aria-label', msg);
btnVmode.setAttribute('tt', msg + tts); btnVmode.setAttribute('tt', msg + tts);
btnVmode.textContent = lbl; btnVmode.textContent = lbl;
swrite('vmode', lbl[0]);
v.loop = vloop v.loop = vloop
if (vloop && v.paused) if (vloop && v.paused)

View File

@@ -8,7 +8,7 @@
--fg: #ccc; --fg: #ccc;
--fg-max: #fff; --fg-max: #fff;
--fg2-max: #fff; --fg2-max: #fff;
--fg-weak: #aaa; --fg-weak: #bbb;
--bg-u7: #555; --bg-u7: #555;
--bg-u6: #4c4c4c; --bg-u6: #4c4c4c;
@@ -18,7 +18,7 @@
--bg-u2: #2b2b2b; --bg-u2: #2b2b2b;
--bg-u1: #282828; --bg-u1: #282828;
--bg: #222; --bg: #222;
--bgg: #222; --bgg: var(--bg);
--bg-d1: #1c1c1c; --bg-d1: #1c1c1c;
--bg-d2: #181818; --bg-d2: #181818;
--bg-d3: #111; --bg-d3: #111;
@@ -37,7 +37,7 @@
--btn-fg: var(--a); --btn-fg: var(--a);
--btn-bg: rgba(128,128,128,0.15); --btn-bg: rgba(128,128,128,0.15);
--btn-h-fg: var(--a); --btn-h-fg: var(--a-hil);
--btn-h-bg: #805; --btn-h-bg: #805;
--btn-1-fg: #400; --btn-1-fg: #400;
--btn-1-bg: var(--a); --btn-1-bg: var(--a);
@@ -45,9 +45,11 @@
--btn-1h-bg: #fe8; --btn-1h-bg: #fe8;
--chk-fg: var(--tab-alt); --chk-fg: var(--tab-alt);
--txt-sh: var(--bg-d2); --txt-sh: var(--bg-d2);
--txt-bg: var(--btn-bg);
--op-aa-fg: var(--a); --op-aa-fg: var(--a);
--op-aa-bg: var(--bg-d2); --op-aa-bg: var(--bg-d2);
--op-a-sh: rgba(0,0,0,0.5);
--u2-btn-b1: #999; --u2-btn-b1: #999;
--u2-sbtn-b1: #999; --u2-sbtn-b1: #999;
@@ -124,7 +126,7 @@
html.y { html.y {
color-scheme: light; color-scheme: light;
--fg: #333; --fg: #222;
--fg-max: #000; --fg-max: #000;
--fg-weak: #555; --fg-weak: #555;
@@ -132,7 +134,6 @@ html.y {
--bg-d2: #fff; --bg-d2: #fff;
--bg-d1: #fff; --bg-d1: #fff;
--bg: #eaeaea; --bg: #eaeaea;
--bgg: #eaeaea;
--bg-u1: #fff; --bg-u1: #fff;
--bg-u2: #f7f7f7; --bg-u2: #f7f7f7;
--bg-u3: #eaeaea; --bg-u3: #eaeaea;
@@ -141,24 +142,28 @@ html.y {
--bg-u6: #ddd; --bg-u6: #ddd;
--bg-max: #fff; --bg-max: #fff;
--tab-alt: #d38; --tab-alt: #c07;
--row-alt: #f2f2f2; --row-alt: #f2f2f2;
--scroll: #490; --scroll: #490;
--a: #07a; --a: #06a;
--a-b: #08b; --a-b: #08b;
--a-hil: #058; --a-hil: #058;
--a-gray: #bbb; --a-gray: #bbb;
--a-dark: #c0f; --a-dark: #c0f;
--btn-fg: #555; --btn-fg: #555;
--btn-h-fg: #222;
--btn-h-bg: #caf; --btn-h-bg: #caf;
--btn-1-fg: #fff; --btn-1-fg: #fff;
--btn-1-bg: #4a0; --btn-1-bg: #4a0;
--btn-1h-bg: #5c0; --btn-1h-bg: #5c0;
--chk-fg: var(--fg); --chk-fg: var(--fg);
--txt-sh: #888; --txt-sh: #aaa;
--txt-bg: rgba(255,255,255,0.6);
--op-a-sh: #fff;
--u2-txt-bg: var(--bg-max); --u2-txt-bg: var(--bg-max);
--u2-tab-1-sh: #0ad; --u2-tab-1-sh: #0ad;
@@ -171,7 +176,7 @@ html.y {
--sort-1: #059; --sort-1: #059;
--sort-2: #f5d; --sort-2: #f5d;
--srv-1: #666; --srv-1: #555;
--srv-2: #c83; --srv-2: #c83;
--srv-3: #c0a; --srv-3: #c0a;
--srv-3b: rgba(255,68,204,0.6); --srv-3b: rgba(255,68,204,0.6);
@@ -197,6 +202,8 @@ html.y {
--fm-off: #c4a; --fm-off: #c4a;
--mp-sh: #bbb; --mp-sh: #bbb;
--mp-b-bg: transparent; --mp-b-bg: transparent;
text-shadow: none;
} }
html.a { html.a {
--op-aa-sh: 0 0 .2em var(--bg-d3) inset; --op-aa-sh: 0 0 .2em var(--bg-d3) inset;
@@ -225,8 +232,6 @@ html.b {
--g-g1: var(--bg); --g-g1: var(--bg);
--g-sh: rgba(0,0,0,0); --g-sh: rgba(0,0,0,0);
--btn-h-fg: #fff;
--op-aa-bg: rgba(255,255,255,0.06); --op-aa-bg: rgba(255,255,255,0.06);
--u2-sbtn-b1: #fc0; --u2-sbtn-b1: #fc0;
@@ -242,13 +247,13 @@ html.b {
--f-sh1: 0.1; --f-sh1: 0.1;
} }
html.bz { html.bz {
--fg: #bbd; --fg: #cce;
--fg-weak: #bbd;
--bg-u5: #3b3f58; --bg-u5: #3b3f58;
--bg-u4: #1e2130; --bg-u4: #1e2130;
--bg-u3: #1e2130; --bg-u3: #1e2130;
--bg-u1: #1e2130; --bg-u1: #1e2130;
--bg: #11121d; --bg: #11121d;
--bgg: #11121d;
--bg-d1: #232536; --bg-d1: #232536;
--bg-d2: #34384e; --bg-d2: #34384e;
--bg-d3: #34384e; --bg-d3: #34384e;
@@ -260,6 +265,7 @@ html.bz {
--btn-1-bg: #ba2959; --btn-1-bg: #ba2959;
--btn-1-fg: #fff; --btn-1-fg: #fff;
--btn-1h-fg: #000; --btn-1h-fg: #000;
--txt-sh: a;
--u2-tab-1-fg: var(--fg-max); --u2-tab-1-fg: var(--fg-max);
--u2-tab-1-bg: var(--bg); --u2-tab-1-bg: var(--bg);
@@ -273,9 +279,13 @@ html.bz {
--mp-sh: #11121d; --mp-sh: #11121d;
} }
html.by { html.by {
--bg: #f2f2f2;
--row-alt: #f9f9f9;
--scroll: var(--a); --scroll: var(--a);
--btn-1-bg: var(--a); --btn-1-bg: #07a;
--btn-1h-bg: var(--a-hil); --btn-1h-bg: var(--a-hil);
--op-aa-bg: #fff; --op-aa-bg: #fff;
@@ -288,11 +298,10 @@ html.by {
html.c { html.c {
font-weight: bold; font-weight: bold;
--fg: #fff; --fg: #fff;
--fg-weak: #5df; --fg-weak: #cef;
--bg-u5: #409; --bg-u5: #409;
--bg-u2: linear-gradient(-35deg, #fd7233, #cd27a0, #5d47a5 49.5%, #16e9fb 50%, #3b6cc8 50.4%, #0e51ac); --bg-u2: linear-gradient(-35deg, #fd7233, #cd27a0, #5d47a5 49.5%, #16e9fb 50%, #3b6cc8 50.4%, #0e51ac);
--bg: #37235d; --bg: #37235d;
--bgg: var(--bg-u2);
--bg-u3: #407; --bg-u3: #407;
--a: #f9dc22; --a: #f9dc22;
@@ -312,11 +321,13 @@ html.c {
--srv-1: #ea0; --srv-1: #ea0;
} }
html.cz {
--bgg: var(--bg-u2);
}
html.cy { html.cy {
--fg: #fff; --fg: #fff;
--fg-weak: #fff; --fg-weak: #fff;
--bg: #ff0; --bg: #ff0;
--bgg: #ff0;
--bg-u2: #f00; --bg-u2: #f00;
--bg-u3: #f00; --bg-u3: #f00;
--bg-u5: #999; --bg-u5: #999;
@@ -333,6 +344,7 @@ html.cy {
--btn-bg: #000; --btn-bg: #000;
--btn-fg: #ff0; --btn-fg: #ff0;
--btn-h-fg: #fff;
--btn-1-bg: #ff0; --btn-1-bg: #ff0;
--btn-1-fg: #000; --btn-1-fg: #000;
--chk-fg: #fd0; --chk-fg: #fd0;
@@ -360,9 +372,6 @@ html {
font-family: sans-serif; font-family: sans-serif;
text-shadow: 1px 1px 0px var(--bg-max); text-shadow: 1px 1px 0px var(--bg-max);
} }
html.y {
text-shadow: none;
}
html, body { html, body {
margin: 0; margin: 0;
padding: 0; padding: 0;
@@ -580,11 +589,15 @@ html.y #path a:hover {
#acc_info { #acc_info {
color: var(--srv-2); color: var(--srv-2);
background: var(--bg); background: var(--bg);
white-space: nowrap;
} }
#srv_info { #srv_info,
#acc_info {
position: absolute; position: absolute;
font-size: .8em; font-size: .8em;
top: .5em; top: .5em;
}
#srv_info {
left: 2em; left: 2em;
padding-right: .5em; padding-right: .5em;
} }
@@ -597,10 +610,6 @@ html.y #path a:hover {
display: none; display: none;
} }
#acc_info { #acc_info {
position: absolute;
white-space: nowrap;
font-size: .81em;
top: .5em;
right: 2em; right: 2em;
} }
#acc_info > span:not([id]) { #acc_info > span:not([id]) {
@@ -976,7 +985,7 @@ html.y #widget.open {
} }
#ops a { #ops a {
color: var(--a); color: var(--a);
text-shadow: 1px 1px 1px rgba(0,0,0,0.5); text-shadow: 1px 1px 1px var(--op-a-sh);
font-size: 1.5em; font-size: 1.5em;
padding: .25em .4em; padding: .25em .4em;
margin: 0; margin: 0;
@@ -1015,7 +1024,7 @@ html.y #ops svg circle {
} }
.opview input[type=text] { .opview input[type=text] {
color: var(--fg); color: var(--fg);
background: var(--btn-bg); background: var(--txt-bg);
border: none; border: none;
box-shadow: 0 0 2px var(--txt-sh); box-shadow: 0 0 2px var(--txt-sh);
border-bottom: 1px solid #999; border-bottom: 1px solid #999;
@@ -1075,7 +1084,6 @@ input.eq_gain {
#srch_form { #srch_form {
margin-bottom: 0;
padding: 0 .5em .5em 0; padding: 0 .5em .5em 0;
} }
#srch_form table { #srch_form table {
@@ -1192,7 +1200,7 @@ html {
} }
#treeul { #treeul {
position: relative; position: relative;
left: -2em; left: -2.2em;
width: calc(100% + 2em); width: calc(100% + 2em);
} }
.btn { .btn {
@@ -1216,6 +1224,7 @@ html.ca .btn {
border-bottom: .2em solid #709; border-bottom: .2em solid #709;
} }
.btn:hover { .btn:hover {
color: var(--btn-h-fg);
background: var(--btn-h-bg); background: var(--btn-h-bg);
} }
.tgl.btn.on { .tgl.btn.on {
@@ -1245,9 +1254,10 @@ html.ca .tgl.btn.on {
} }
#tree ul { #tree ul {
border-left: .2em solid var(--bg-u5); border-left: .2em solid var(--bg-u5);
margin-left: .44em;
} }
#tree li { #tree li {
margin-left: 1em; margin-left: .6em;
list-style: none; list-style: none;
border-top: 1px solid var(--bg-u5); border-top: 1px solid var(--bg-u5);
} }
@@ -1271,7 +1281,7 @@ html.ca .tgl.btn.on {
display: inline-block; display: inline-block;
} }
.ntree a+a { .ntree a+a {
width: calc(100% - 2em); width: calc(100% - 2.2em);
line-height: 1em; line-height: 1em;
} }
#tree.nowrap li { #tree.nowrap li {
@@ -1348,6 +1358,7 @@ html.y #tree.nowrap .ntree a+a:hover {
line-height: 2em; line-height: 2em;
} }
.opwide>div>h3 { .opwide>div>h3 {
color: var(--fg-weak);
margin: 0 .4em; margin: 0 .4em;
padding: 0; padding: 0;
} }
@@ -1838,27 +1849,29 @@ html.y #bbox-overlay figcaption a {
position: absolute; position: absolute;
top: 40%; top: 40%;
top: calc(50% - .5em); top: calc(50% - .5em);
top: calc(50% - 1.2vw);
left: -.8em; left: -.8em;
transition: top 0.12s;
} }
.dropdesc>div>div+div { .dropdesc>div>div+div {
left: auto; left: auto;
right: -.8em; right: -.8em;
} }
.dropdesc b { .dropdesc b {
position: relative;
font-size: .5em; font-size: .5em;
font-size: 2vw; font-size: 2vw;
top: -.25em;
top: -.2vw;
margin: 0 .8em; margin: 0 .8em;
margin: 0 1.25vw; margin: 0 1.25vw;
transition: font-size 0.12s; transition: font-size 0.12s;
} }
.dropdesc.hl b { .dropdesc.hl.ok b {
border-bottom: .1em solid #fff; border-bottom: .1em solid #fff;
font-size: .6em; font-size: .6em;
font-size: 2.5vw; font-size: 2.5vw;
} }
.dropdesc.hl.ok>div>div {
top: calc(50% - 1.7vw);
}
.dropzone { .dropzone {
z-index: 80386; z-index: 80386;
height: 50%; height: 50%;
@@ -2277,6 +2290,11 @@ html.c #treeh,
html.a #tree, html.a #tree,
html.a #treeh { html.a #treeh {
border-radius: 0 .3em 0 0; border-radius: 0 .3em 0 0;
background: var(--bg-u2);
}
html.c #treepar,
html.a #treepar {
background: var(--bg-u2);
} }
html.c #tree li, html.c #tree li,
html.a #tree li { html.a #tree li {
@@ -2306,15 +2324,6 @@ html.a .ghead {
border: 1px solid var(--bg-u3); border: 1px solid var(--bg-u3);
box-shadow: 0 0 .3em var(--bg-d3); box-shadow: 0 0 .3em var(--bg-d3);
} }
html.c #tree,
html.c #treeh,
html.c #treepar,
html.a #tree,
html.a #treeh,
html.a #treepar {
background: var(--bg-u2);
}
html.c #u2btn, html.c #u2btn,
html.a #u2btn { html.a #u2btn {
color: #eee; color: #eee;

View File

@@ -68,7 +68,7 @@
<div id="op_cfg" class="opview opbox opwide"></div> <div id="op_cfg" class="opview opbox opwide"></div>
<h1 id="path"> <h1 id="path">
<a href="#" id="entree" tt="show navpane (directory tree sidebar)$NHotkey: B">🌲</a> <a href="#" id="entree">🌲</a>
{%- for n in vpnodes %} {%- for n in vpnodes %}
<a href="/{{ n[0] }}">{{ n[1] }}</a> <a href="/{{ n[0] }}">{{ n[1] }}</a>
{%- endfor %} {%- endfor %}
@@ -120,7 +120,7 @@
<div id="epi" class="logue">{{ logues[1] }}</div> <div id="epi" class="logue">{{ logues[1] }}</div>
<h2><a href="/?h">control-panel</a></h2> <h2><a href="/?h" id="goh">control-panel</a></h2>
<a href="#" id="repl">π</a> <a href="#" id="repl">π</a>
@@ -138,6 +138,7 @@
themes = {{ themes }}, themes = {{ themes }},
dtheme = "{{ dtheme }}", dtheme = "{{ dtheme }}",
srvinf = "{{ srv_info }}", srvinf = "{{ srv_info }}",
lang = "{{ lang }}",
def_hcols = {{ def_hcols|tojson }}, def_hcols = {{ def_hcols|tojson }},
have_up2k_idx = {{ have_up2k_idx|tojson }}, have_up2k_idx = {{ have_up2k_idx|tojson }},
have_tags_idx = {{ have_tags_idx|tojson }}, have_tags_idx = {{ have_tags_idx|tojson }},

File diff suppressed because it is too large Load Diff

View File

@@ -258,7 +258,7 @@ function Modpoll() {
var xhr = new XHR(); var xhr = new XHR();
xhr.open('GET', url, true); xhr.open('GET', url, true);
xhr.responseType = 'text'; xhr.responseType = 'text';
xhr.onreadystatechange = r.cb; xhr.onload = xhr.onerror = r.cb;
xhr.send(); xhr.send();
}; };
@@ -268,9 +268,6 @@ function Modpoll() {
return; return;
} }
if (this.readyState != XHR.DONE)
return;
if (this.status !== 200) { if (this.status !== 200) {
console.log('modpoll err ' + this.status + ": " + this.responseText); console.log('modpoll err ' + this.status + ": " + this.responseText);
return; return;
@@ -339,7 +336,7 @@ function save(e) {
var xhr = new XHR(); var xhr = new XHR();
xhr.open('POST', url, true); xhr.open('POST', url, true);
xhr.responseType = 'text'; xhr.responseType = 'text';
xhr.onreadystatechange = save_cb; xhr.onload = xhr.onerror = save_cb;
xhr.btn = save_btn; xhr.btn = save_btn;
xhr.txt = txt; xhr.txt = txt;
@@ -356,9 +353,6 @@ function save(e) {
} }
function save_cb() { function save_cb() {
if (this.readyState != XHR.DONE)
return;
if (this.status !== 200) if (this.status !== 200)
return toast.err(0, 'Error! The file was NOT saved.\n\n' + this.status + ": " + (this.responseText + '').replace(/^<pre>/, "")); return toast.err(0, 'Error! The file was NOT saved.\n\n' + this.status + ": " + (this.responseText + '').replace(/^<pre>/, ""));
@@ -400,7 +394,7 @@ function run_savechk(lastmod, txt, btn, ntry) {
var xhr = new XHR(); var xhr = new XHR();
xhr.open('GET', url, true); xhr.open('GET', url, true);
xhr.responseType = 'text'; xhr.responseType = 'text';
xhr.onreadystatechange = savechk_cb; xhr.onload = xhr.onerror = savechk_cb;
xhr.lastmod = lastmod; xhr.lastmod = lastmod;
xhr.txt = txt; xhr.txt = txt;
xhr.btn = btn; xhr.btn = btn;
@@ -409,9 +403,6 @@ function run_savechk(lastmod, txt, btn, ntry) {
} }
function savechk_cb() { function savechk_cb() {
if (this.readyState != XHR.DONE)
return;
if (this.status !== 200) if (this.status !== 200)
return toast.err(0, 'Error! The file was NOT saved.\n\n' + this.status + ": " + (this.responseText + '').replace(/^<pre>/, "")); return toast.err(0, 'Error! The file was NOT saved.\n\n' + this.status + ": " + (this.responseText + '').replace(/^<pre>/, ""));

View File

@@ -117,7 +117,7 @@ function save(mde) {
var xhr = new XHR(); var xhr = new XHR();
xhr.open('POST', url, true); xhr.open('POST', url, true);
xhr.responseType = 'text'; xhr.responseType = 'text';
xhr.onreadystatechange = save_cb; xhr.onload = xhr.onerror = save_cb;
xhr.btn = save_btn; xhr.btn = save_btn;
xhr.mde = mde; xhr.mde = mde;
xhr.txt = txt; xhr.txt = txt;
@@ -133,9 +133,6 @@ function save(mde) {
} }
function save_cb() { function save_cb() {
if (this.readyState != XHR.DONE)
return;
if (this.status !== 200) if (this.status !== 200)
return toast.err(0, 'Error! The file was NOT saved.\n\n' + this.status + ": " + (this.responseText + '').replace(/^<pre>/, "")); return toast.err(0, 'Error! The file was NOT saved.\n\n' + this.status + ": " + (this.responseText + '').replace(/^<pre>/, ""));
@@ -173,7 +170,7 @@ function save_cb() {
var xhr = new XHR(); var xhr = new XHR();
xhr.open('GET', url, true); xhr.open('GET', url, true);
xhr.responseType = 'text'; xhr.responseType = 'text';
xhr.onreadystatechange = save_chk; xhr.onload = xhr.onerror = save_chk;
xhr.btn = this.save_btn; xhr.btn = this.save_btn;
xhr.mde = this.mde; xhr.mde = this.mde;
xhr.txt = this.txt; xhr.txt = this.txt;
@@ -182,9 +179,6 @@ function save_cb() {
} }
function save_chk() { function save_chk() {
if (this.readyState != XHR.DONE)
return;
if (this.status !== 200) if (this.status !== 200)
return toast.err(0, 'Error! The file was NOT saved.\n\n' + this.status + ": " + (this.responseText + '').replace(/^<pre>/, "")); return toast.err(0, 'Error! The file was NOT saved.\n\n' + this.status + ": " + (this.responseText + '').replace(/^<pre>/, ""));

View File

@@ -1,9 +1,7 @@
html, body, #wrap { html {
color: #333; color: #333;
background: #f7f7f7; background: #f7f7f7;
font-family: sans-serif; font-family: sans-serif;
}
html {
touch-action: manipulation; touch-action: manipulation;
} }
#wrap { #wrap {
@@ -38,7 +36,6 @@ a+a {
margin: -.2em 0 0 .5em; margin: -.2em 0 0 .5em;
} }
.logout, .logout,
.btns a,
a.r { a.r {
color: #c04; color: #c04;
border-color: #c7a; border-color: #c7a;
@@ -88,9 +85,7 @@ blockquote {
} }
html.z, html.z {
html.z body,
html.z #wrap {
background: #222; background: #222;
color: #ccc; color: #ccc;
} }
@@ -103,7 +98,6 @@ html.z a {
border-color: #37a; border-color: #37a;
} }
html.z .logout, html.z .logout,
html.z .btns a,
html.z a.r { html.z a.r {
background: #804; background: #804;
border-color: #c28; border-color: #c28;
@@ -120,3 +114,9 @@ html.z input {
html.z .num { html.z .num {
border-color: #777; border-color: #777;
} }
html.bz {
color: #bbd;
background: #11121d;
}

View File

@@ -13,13 +13,13 @@
<body> <body>
<div id="wrap"> <div id="wrap">
<a href="/?h" class="refresh">refresh</a> <a id="a" href="/?h" class="refresh">refresh</a>
{%- if this.uname == '*' %} {%- if this.uname == '*' %}
<p>howdy stranger &nbsp; <small>(you're not logged in)</small></p> <p id="b">howdy stranger &nbsp; <small>(you're not logged in)</small></p>
{%- else %} {%- else %}
<a href="/?pw=x" class="logout">logout</a> <a id="c" href="/?pw=x" class="logout">logout</a>
<p>welcome back, <strong>{{ this.uname }}</strong></p> <p><span id="m">welcome back,</span> <strong>{{ this.uname }}</strong></p>
{%- endif %} {%- endif %}
{%- if msg %} {%- if msg %}
@@ -39,24 +39,24 @@
</table> </table>
</td><td> </td><td>
<table class="vols"> <table class="vols">
<thead><tr><th>vol</th><th>action</th><th>status</th></tr></thead> <thead><tr><th>vol</th><th id="t">action</th><th>status</th></tr></thead>
<tbody> <tbody>
{% for mp in avol %} {% for mp in avol %}
{%- if mp in vstate and vstate[mp] %} {%- if mp in vstate and vstate[mp] %}
<tr><td><a href="{{ mp }}{{ url_suf }}">{{ mp }}</a></td><td><a href="{{ mp }}?scan">rescan</a></td><td>{{ vstate[mp] }}</td></tr> <tr><td><a href="{{ mp }}{{ url_suf }}">{{ mp }}</a></td><td><a class="s" href="{{ mp }}?scan">rescan</a></td><td>{{ vstate[mp] }}</td></tr>
{%- endif %} {%- endif %}
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</td></tr></table> </td></tr></table>
<div class="btns"> <div class="btns">
<a href="/?stack" tt="shows the state of all active threads">dump stack</a> <a id="d" href="/?stack" tt="shows the state of all active threads">dump stack</a>
<a href="/?reload=cfg" tt="reload config files (accounts/volumes/volflags),$Nand rescan all e2ds volumes">reload cfg</a> <a id="e" href="/?reload=cfg" tt="reload config files (accounts/volumes/volflags),$Nand rescan all e2ds volumes">reload cfg</a>
</div> </div>
{%- endif %} {%- endif %}
{%- if rvol %} {%- if rvol %}
<h1>you can browse these:</h1> <h1 id="f">you can browse:</h1>
<ul> <ul>
{% for mp in rvol %} {% for mp in rvol %}
<li><a href="{{ mp }}{{ url_suf }}">{{ mp }}</a></li> <li><a href="{{ mp }}{{ url_suf }}">{{ mp }}</a></li>
@@ -65,7 +65,7 @@
{%- endif %} {%- endif %}
{%- if wvol %} {%- if wvol %}
<h1>you can upload to:</h1> <h1 id="g">you can upload to:</h1>
<ul> <ul>
{% for mp in wvol %} {% for mp in wvol %}
<li><a href="{{ mp }}{{ url_suf }}">{{ mp }}</a></li> <li><a href="{{ mp }}{{ url_suf }}">{{ mp }}</a></li>
@@ -76,16 +76,16 @@
<h1 id="cc">client config:</h1> <h1 id="cc">client config:</h1>
<ul> <ul>
{% if k304 %} {% if k304 %}
<li><a href="/?k304=n">disable k304</a> (currently enabled) <li><a id="h" href="/?k304=n">disable k304</a> (currently enabled)
{%- else %} {%- else %}
<li><a href="/?k304=y" class="r">enable k304</a> (currently disabled) <li><a id="i" 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 id="j">enabling this will disconnect your client on every HTTP 304, which can prevent some buggy proxies from getting stuck (suddenly not loading 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> <li><a id="k" href="/?reset" class="r" onclick="localStorage.clear();return true">reset client settings</a></li>
</ul> </ul>
<h1>login for more:</h1> <h1 id="l">login for more:</h1>
<ul> <ul>
<form method="post" enctype="multipart/form-data" action="/{{ qvpath }}"> <form method="post" enctype="multipart/form-data" action="/{{ qvpath }}">
<input type="hidden" name="act" value="login" /> <input type="hidden" name="act" value="login" />
@@ -97,15 +97,11 @@
<a href="#" id="repl">π</a> <a href="#" id="repl">π</a>
<script> <script>
document.documentElement.className = localStorage.light == 1 ? "y" : "z"; var lang="{{ this.args.lang }}";
document.documentElement.className=localStorage.theme||"{{ this.args.theme }}";
</script> </script>
<script src="/.cpr/util.js?_={{ ts }}"></script> <script src="/.cpr/util.js?_={{ ts }}"></script>
<script> <script src="/.cpr/splash.js?_={{ ts }}"></script>
tt.init();
{%- if this.uname == '*' %}
QS('input[name="cppwd"]').focus();
{%- endif %}
</script>
</body> </body>
</html> </html>

44
copyparty/web/splash.js Normal file
View File

@@ -0,0 +1,44 @@
var Ls = {
"nor": {
"a1": "oppdater",
"b1": "halloien &nbsp; <small>(du er ikke logget inn)</small>",
"c1": "logg ut",
"d1": "tilstand",
"d2": "vis tilstanden til alle tråder",
"e1": "last innst.",
"e2": "leser inn konfigurasjonsfiler på nytt$N(kontoer, volumer, volumbrytere)$Nog kartlegger alle e2ds-volumer",
"f1": "du kan betrakte:",
"g1": "du kan laste opp til:",
"cc1": "klient-konfigurasjon",
"h1": "skru av k304",
"i1": "skru på k304",
"j1": "k304 bryter tilkoplingen for hver HTTP 304. Dette hjelper visse mellomtjenere som kan sette seg fast / plutselig slutter å laste sider, men det reduserer også ytelsen betydelig",
"k1": "nullstill innstillinger",
"l1": "logg inn:",
"m1": "velkommen tilbake,",
"n1": "404: filen finnes ikke &nbsp;┐( ´ -`)┌",
"o1": 'eller kanskje du ikke har tilgang? prøv å logge inn eller <a href="/?h">gå hjem</a>',
"p1": "403: tilgang nektet &nbsp;~┻━┻",
"q1": 'du må logge inn eller <a href="/?h">gå hjem</a>',
"r1": "gå hjem",
".s1": "kartlegg",
"t1": "handling",
}
},
d = Ls[sread("lang") || lang];
for (var k in (d || {})) {
var f = k.slice(-1),
i = k.slice(0, -1),
o = QSA(i.startsWith('.') ? i : '#' + i);
for (var a = 0; a < o.length; a++)
if (f == 1)
o[a].innerHTML = d[k];
else if (f == 2)
o[a].setAttribute("tt", d[k]);
}
tt.init();
if (!ebi('c'))
QS('input[name="cppwd"]').focus();

View File

@@ -75,6 +75,9 @@ html {
margin-right: -1.2em; margin-right: -1.2em;
padding-right: .7em; padding-right: .7em;
} }
#toast.r #toastb {
text-align: right;
}
#toast pre { #toast pre {
margin: 0; margin: 0;
} }
@@ -163,15 +166,23 @@ html.y #tt {
background: #fff; background: #fff;
border-color: #888 #000 #777 #000; border-color: #888 #000 #777 #000;
} }
html.bz #tt {
background: #202231;
border-color: #3b3f58;
}
html.y #tt, html.y #tt,
html.y #toast { html.y #toast {
box-shadow: 0 .3em 1em rgba(0,0,0,0.4); box-shadow: 0 .3em 1em rgba(0,0,0,0.4);
} }
#modalc code,
html.y #tt code { html.y #tt code {
background: #060; background: #060;
color: #fff; color: #fff;
} }
#modalc code {
color: #060;
background: transparent;
border: 1px solid #ccc;
}
html.y #tt em { html.y #tt em {
color: #d38; color: #d38;
} }
@@ -478,10 +489,20 @@ html.y textarea:focus {
border-top: .4em solid #b80; border-top: .4em solid #b80;
border-bottom: .4em solid #4c4c4c; border-bottom: .4em solid #4c4c4c;
} }
html.bz .mdo h1 {
background: #202231;
border: 1px solid #2d2f45;
border-width: 0 0 .4em 0;
}
html.z .mdo h2 { html.z .mdo h2 {
background: #444; background: #444;
border-bottom: .22em solid #555; border-bottom: .22em solid #555;
} }
html.bz .mdo h2,
html.bz .mdo h3 {
background: transparent;
border-color: #3b3f58;
}
html.z .mdo td, html.z .mdo td,
html.z .mdo th { html.z .mdo th {
border-color: #444; border-color: #444;

View File

@@ -568,12 +568,12 @@ function Donut(uc, st) {
function fsearch_explain(n) { function fsearch_explain(n) {
if (n) if (n)
return toast.inf(60, 'your access to this folder is Read-Only\n\n' + (acct == '*' ? 'you are currently not logged in' : 'you are currently logged in as "' + acct + '"')); return toast.inf(60, L.ue_ro + (acct == '*' ? L.ue_nl : L.ue_la).format(acct));
if (bcfg_get('fsearch', false)) if (bcfg_get('fsearch', false))
return toast.inf(60, 'you are currently in file-search mode\n\nswitch to upload-mode by clicking the green magnifying glass (next to the big yellow search button), and try uploading again\n\nsorry'); return toast.inf(60, L.ue_sr);
return toast.inf(60, 'try again, it should work now'); return toast.inf(60, L.ue_ta);
} }
@@ -592,14 +592,7 @@ function up2k_init(subtle) {
ebi('u2notbtn').innerHTML = ''; ebi('u2notbtn').innerHTML = '';
} }
var suggest_up2k = 'this is the basic uploader; <a href="#" id="u2yea">up2k</a> is better'; var suggest_up2k = L.u_su2k;
var shame = 'your browser <a href="https://www.chromium.org/blink/webcrypto">disables sha512</a> unless you <a href="' + (window.location + '').replace(':', 's:') + '">use https</a>',
is_https = (window.location + '').indexOf('https:') === 0;
if (is_https)
// chrome<37 firefox<34 edge<12 opera<24 safari<7
shame = 'your browser is impressively ancient';
function got_deps() { function got_deps() {
return subtle || window.asmCrypto || window.hashwasm; return subtle || window.asmCrypto || window.hashwasm;
@@ -608,15 +601,18 @@ function up2k_init(subtle) {
var loading_deps = false; var loading_deps = false;
function init_deps() { function init_deps() {
if (!loading_deps && !got_deps()) { if (!loading_deps && !got_deps()) {
var fn = 'sha512.' + sha_js + '.js'; var fn = 'sha512.' + sha_js + '.js',
showmodal('<h1>loading ' + fn + '</h1><h2>since ' + shame + '</h2><h4>thanks chrome</h4>'); m = L.u_https1 + ' <a href="' + (window.location + '').replace(':', 's:') + '">' + L.u_https2 + '</a> ' + L.u_https3;
showmodal('<h1>loading ' + fn + '</h1>');
import_js('/.cpr/deps/' + fn, unmodal); import_js('/.cpr/deps/' + fn, unmodal);
if (is_https) if (is_https) {
ebi('u2foot').innerHTML = shame + ' so <em>this</em> uploader will do like 500 KiB/s at best'; // chrome<37 firefox<34 edge<12 opera<24 safari<7
else m = L.u_ancient;
ebi('u2foot').innerHTML = 'seems like ' + shame + ' so do that if you want more performance <span style="color:#' + setmsg('');
(sha_js == 'ac' ? 'c84">(expecting 20' : '8a5">(but dont worry too much, expect 100') + ' MiB/s)</span>'; }
ebi('u2foot').innerHTML = '<big>' + m + '</big>';
} }
loading_deps = true; loading_deps = true;
} }
@@ -648,16 +644,6 @@ function up2k_init(subtle) {
setmsg(suggest_up2k, 'msg'); setmsg(suggest_up2k, 'msg');
if (!String.prototype.format) {
String.prototype.format = function () {
var args = arguments;
return this.replace(/{(\d+)}/g, function (match, number) {
return typeof args[number] != 'undefined' ?
args[number] : match;
});
};
}
var parallel_uploads = icfg_get('nthread'), var parallel_uploads = icfg_get('nthread'),
uc = {}, uc = {},
fdom_ctr = 0, fdom_ctr = 0,
@@ -715,7 +701,7 @@ function up2k_init(subtle) {
bobslice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice; bobslice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
if (!bobslice || !window.FileReader || !window.FileList) if (!bobslice || !window.FileReader || !window.FileList)
return un2k("this is the basic uploader; up2k needs at least<br />chrome 21 // firefox 13 // edge 12 // opera 12 // safari 5.1"); return un2k(L.u_ever);
var flag = false; var flag = false;
apply_flag_cfg(); apply_flag_cfg();
@@ -737,14 +723,14 @@ function up2k_init(subtle) {
var mup, up = QS('#up_zd'); var mup, up = QS('#up_zd');
var msr, sr = QS('#srch_zd'); var msr, sr = QS('#srch_zd');
if (!has(perms, 'write')) if (!has(perms, 'write'))
mup = 'you do not have write-access to this folder'; mup = L.u_ewrite;
if (!has(perms, 'read')) if (!has(perms, 'read'))
msr = 'you do not have read-access to this folder'; msr = L.u_eread;
if (!have_up2k_idx) if (!have_up2k_idx)
msr = 'file-search is not enabled in server config'; msr = L.u_enoi;
up.querySelector('span').textContent = mup || 'drop it here'; up.querySelector('span').textContent = mup || L.udt_drop;
sr.querySelector('span').textContent = msr || 'drop it here'; sr.querySelector('span').textContent = msr || L.udt_drop;
clmod(up, 'err', mup); clmod(up, 'err', mup);
clmod(sr, 'err', msr); clmod(sr, 'err', msr);
clmod(up, 'ok', !mup); clmod(up, 'ok', !mup);
@@ -970,22 +956,22 @@ function up2k_init(subtle) {
function gotallfiles(good_files, nil_files, bad_files) { function gotallfiles(good_files, nil_files, bad_files) {
var ntot = good_files.concat(nil_files, bad_files).length; var ntot = good_files.concat(nil_files, bad_files).length;
if (bad_files.length) { if (bad_files.length) {
var msg = 'These {0} files (of {1} total) were skipped, possibly due to filesystem permissions:\n'.format(bad_files.length, ntot); var msg = L.u_badf.format(bad_files.length, ntot);
for (var a = 0, aa = Math.min(20, bad_files.length); a < aa; a++) for (var a = 0, aa = Math.min(20, bad_files.length); a < aa; a++)
msg += '-- ' + bad_files[a][1] + '\n'; msg += '-- ' + bad_files[a][1] + '\n';
msg += '\nMaybe it works better if you select just one file'; msg += L.u_just1;
return modal.alert(msg, function () { return modal.alert(msg, function () {
gotallfiles(good_files, nil_files, []); gotallfiles(good_files, nil_files, []);
}); });
} }
if (nil_files.length) { if (nil_files.length) {
var msg = 'These {0} files (of {1} total) are blank/empty; upload them anyways?\n'.format(nil_files.length, ntot); var msg = L.u_blankf.format(nil_files.length, ntot);
for (var a = 0, aa = Math.min(20, nil_files.length); a < aa; a++) for (var a = 0, aa = Math.min(20, nil_files.length); a < aa; a++)
msg += '-- ' + nil_files[a][1] + '\n'; msg += '-- ' + nil_files[a][1] + '\n';
msg += '\nMaybe it works better if you select just one file'; msg += L.u_just1;
return modal.confirm(msg, function () { return modal.confirm(msg, function () {
gotallfiles(good_files.concat(nil_files), [], []); gotallfiles(good_files.concat(nil_files), [], []);
}, function () { }, function () {
@@ -999,12 +985,15 @@ function up2k_init(subtle) {
return a < b ? -1 : a > b ? 1 : 0; return a < b ? -1 : a > b ? 1 : 0;
}); });
var msg = ['{0} these {1} files?<ul>'.format(uc.fsearch ? 'search' : 'upload', good_files.length)]; var msg = [L.u_asku.format(good_files.length, esc(get_vpath())) + '<ul>'];
for (var a = 0, aa = Math.min(20, good_files.length); a < aa; a++) for (var a = 0, aa = Math.min(20, good_files.length); a < aa; a++)
msg.push('<li>' + esc(good_files[a][1]) + '</li>'); msg.push('<li>' + esc(good_files[a][1]) + '</li>');
if (uc.ask_up && !uc.fsearch) if (uc.ask_up && !uc.fsearch)
return modal.confirm(msg.join('') + '</ul>', function () { up_them(good_files); }, null); return modal.confirm(msg.join('') + '</ul>', function () {
up_them(good_files);
toast.inf(15, L.u_unpt);
}, null);
up_them(good_files); up_them(good_files);
} }
@@ -1056,7 +1045,7 @@ function up2k_init(subtle) {
pvis.addfile([ pvis.addfile([
uc.fsearch ? esc(entry.name) : linksplit( uc.fsearch ? esc(entry.name) : linksplit(
entry.purl + uricom_enc(entry.name)).join(' '), entry.purl + uricom_enc(entry.name)).join(' '),
'📐 hash', '📐 ' + L.u_hashing,
'' ''
], fobj.size, draw_each); ], fobj.size, draw_each);
@@ -1104,11 +1093,11 @@ function up2k_init(subtle) {
} }
if (!nhash) if (!nhash)
ebi('u2etah').innerHTML = 'Done ({0}, {1} files)'.format(humansize(st.bytes.hashed), pvis.ctr["ok"] + pvis.ctr["ng"]); ebi('u2etah').innerHTML = L.u_etadone.format(humansize(st.bytes.hashed), pvis.ctr["ok"] + pvis.ctr["ng"]);
if (!nsend && !nhash) if (!nsend && !nhash)
ebi('u2etau').innerHTML = ebi('u2etat').innerHTML = ( ebi('u2etau').innerHTML = ebi('u2etat').innerHTML = (
'Done ({0}, {1} files)'.format(humansize(st.bytes.uploaded), pvis.ctr["ok"] + pvis.ctr["ng"])); L.u_etadone.format(humansize(st.bytes.uploaded), pvis.ctr["ok"] + pvis.ctr["ng"]));
if (!st.busy.hash.length && !hashing_permitted()) if (!st.busy.hash.length && !hashing_permitted())
nhash = 0; nhash = 0;
@@ -1129,7 +1118,7 @@ function up2k_init(subtle) {
} }
if ((nhash || nsend) && !uc.fsearch) { if ((nhash || nsend) && !uc.fsearch) {
if (!st.bytes.finished) { if (!st.bytes.finished) {
ebi('u2etat').innerHTML = '(preparing to upload)'; ebi('u2etat').innerHTML = L.u_etaprep;
} }
else { else {
st.time.busy += td; st.time.busy += td;
@@ -1142,7 +1131,7 @@ function up2k_init(subtle) {
eta = Math.floor(rem / bps); eta = Math.floor(rem / bps);
if (t[a][1] < 1024 || t[a][3] < 0.1) { if (t[a][1] < 1024 || t[a][3] < 0.1) {
ebi(t[a][0]).innerHTML = '(preparing to upload)'; ebi(t[a][0]).innerHTML = L.u_etaprep;
continue; continue;
} }
@@ -1260,24 +1249,21 @@ function up2k_init(subtle) {
donut.on(is_busy); donut.on(is_busy);
if (!is_busy) { if (!is_busy) {
var k = uc.fsearch ? 'searches' : 'uploads', var sr = uc.fsearch,
ks = uc.fsearch ? 'Search' : 'Upload',
tok = uc.fsearch ? 'successful (found on server)' : 'completed successfully',
tng = uc.fsearch ? 'failed (NOT found on server)' : 'failed, sorry',
ok = pvis.ctr["ok"], ok = pvis.ctr["ok"],
ng = pvis.ctr["ng"], ng = pvis.ctr["ng"],
t = uc.ask_up ? 0 : 10; t = uc.ask_up ? 0 : 10;
if (ok && ng) if (ok && ng)
toast.warn(t, 'Finished, but some {0} failed:\n{1} {2},\n{3} {4}'.format(k, ok, tok, ng, tng)); toast.warn(t, (sr ? L.ur_sm : L.ur_um).format(ok, ng));
else if (ok > 1) else if (ok > 1)
toast.ok(t, 'All {1} {0} {2}'.format(k, ok, tok)); toast.ok(t, (sr ? L.ur_aso : L.ur_auo).format(ok));
else if (ok) else if (ok)
toast.ok(t, '{0} {1}'.format(ks, tok)); toast.ok(t, sr ? L.ur_1so : L.ur_1uo);
else if (ng > 1) else if (ng > 1)
toast.err(t, 'All {1} {0} {2}'.format(k, ng, tng)); toast.err(t, (sr ? L.ur_asn : L.ur_aun).format(ng));
else if (ng) else if (ng)
toast.err(t, '{0} {1}'.format(ks, tng)); toast.err(t, sr ? L.ur_1sn : L.ur_1un);
timer.rm(etafun); timer.rm(etafun);
timer.rm(donut.do); timer.rm(donut.do);
@@ -1530,7 +1516,7 @@ function up2k_init(subtle) {
t.t_hashed = Date.now(); t.t_hashed = Date.now();
pvis.seth(t.n, 2, 'hashing done'); pvis.seth(t.n, 2, L.u_hashdone);
pvis.seth(t.n, 1, '📦 wait'); pvis.seth(t.n, 1, '📦 wait');
apop(st.busy.hash, t); apop(st.busy.hash, t);
st.todo.handshake.push(t); st.todo.handshake.push(t);
@@ -1658,8 +1644,8 @@ function up2k_init(subtle) {
if (!response || !response.hits || !response.hits.length) { if (!response || !response.hits || !response.hits.length) {
smsg = '404'; smsg = '404';
msg = ('not found on server <a href="#" onclick="fsearch_explain(' + msg = (L.u_s404 + ' <a href="#" onclick="fsearch_explain(' +
(has(perms, 'write') ? '0' : '1') + ')" class="fsearch_explain">(explain)</a>'); (has(perms, 'write') ? '0' : '1') + ')" class="fsearch_explain">(' + L.u_expl + ')</a>');
} }
else { else {
smsg = 'found'; smsg = 'found';
@@ -1734,7 +1720,7 @@ function up2k_init(subtle) {
'npart': t.postlist[a] 'npart': t.postlist[a]
}); });
msg = 'uploading'; msg = L.u_upping;
done = false; done = false;
if (sort) if (sort)
@@ -1811,8 +1797,8 @@ function up2k_init(subtle) {
tasker(); tasker();
return; return;
} }
err = t.t_uploading ? "finalize upload" : t.srch ? "perform search" : "initiate upload"; err = t.t_uploading ? L.u_ehsfin : t.srch ? L.u_ehssrch : L.u_ehsinit;
xhrchk(xhr, "server rejected the request to " + err + ";\n\nfile: " + t.name + "\n\nerror ", "404, target folder not found"); xhrchk(xhr, err + ";\n\nfile: " + t.name + "\n\nerror ", "404, target folder not found");
} }
} }
xhr.onload = function (e) { xhr.onload = function (e) {
@@ -1871,7 +1857,7 @@ function up2k_init(subtle) {
console.log("ignoring dupe-segment error", t); console.log("ignoring dupe-segment error", t);
} }
else { else {
xhrchk(xhr, "server rejected upload (chunk {0} of {1});\n\nfile: {2}\n\nerror ".format(npart, Math.ceil(t.size / chunksize), t.name), "404, target folder not found (???)"); xhrchk(xhr, L.u_cuerr2.format(npart, Math.ceil(t.size / chunksize), t.name), "404, target folder not found (???)");
chill(t); chill(t);
} }
@@ -1900,7 +1886,7 @@ function up2k_init(subtle) {
return; return;
if (!toast.visible) if (!toast.visible)
toast.warn(9.98, "failed to upload chunk {0} of {1};\nprobably harmless, continuing\n\nfile: {2}".format(npart, Math.ceil(t.size / chunksize), t.name)); toast.warn(9.98, L.u_cuerr.format(npart, Math.ceil(t.size / chunksize), t.name));
console.log('chunkpit onerror,', ++tries, t); console.log('chunkpit onerror,', ++tries, t);
orz2(xhr); orz2(xhr);
@@ -2013,10 +1999,8 @@ function up2k_init(subtle) {
} }
function draw_turbo() { function draw_turbo() {
var msgu = '<p class="warn">WARNING: turbo enabled, <span>&nbsp;client may not detect and resume incomplete uploads; see turbo-button tooltip</span></p>', var msg = uc.fsearch ? L.u_ts : L.u_tu,
msgs = '<p class="warn">WARNING: turbo enabled, <span>&nbsp;search results can be incorrect; see turbo-button tooltip</span></p>', omsg = uc.fsearch ? L.u_tu : L.u_ts,
msg = uc.fsearch ? msgs : msgu,
omsg = uc.fsearch ? msgu : msgs,
html = ebi('u2foot').innerHTML, html = ebi('u2foot').innerHTML,
ohtml = html; ohtml = html;
@@ -2026,7 +2010,7 @@ function up2k_init(subtle) {
if (msg && html.indexOf(msg) === -1) if (msg && html.indexOf(msg) === -1)
html = html.replace(omsg, '') + msg; html = html.replace(omsg, '') + msg;
else if (!msg) else if (!msg)
html = html.replace(msgu, '').replace(msgs, ''); html = html.replace(L.u_tu, '').replace(L.u_ts, '');
if (html !== ohtml) if (html !== ohtml)
ebi('u2foot').innerHTML = html; ebi('u2foot').innerHTML = html;
@@ -2062,7 +2046,7 @@ function up2k_init(subtle) {
try { try {
var ico = uc.fsearch ? '🔎' : '🚀', var ico = uc.fsearch ? '🔎' : '🚀',
desc = uc.fsearch ? 'S E A R C H' : 'U P L O A D'; desc = uc.fsearch ? L.ul_btns : L.ul_btnu;
clmod(ebi('op_up2k'), 'srch', uc.fsearch); clmod(ebi('op_up2k'), 'srch', uc.fsearch);
ebi('u2bm').innerHTML = ico + '&nbsp; <sup>' + desc + '</sup>'; ebi('u2bm').innerHTML = ico + '&nbsp; <sup>' + desc + '</sup>';

View File

@@ -7,6 +7,7 @@ if (!window['console'])
var is_touch = 'ontouchstart' in window, var is_touch = 'ontouchstart' in window,
is_https = (window.location + '').indexOf('https:') === 0,
IPHONE = is_touch && /iPhone|iPad|iPod/i.test(navigator.userAgent), IPHONE = is_touch && /iPhone|iPad|iPod/i.test(navigator.userAgent),
WINDOWS = navigator.platform ? navigator.platform == 'Win32' : /Windows/.test(navigator.userAgent); WINDOWS = navigator.platform ? navigator.platform == 'Win32' : /Windows/.test(navigator.userAgent);
@@ -253,6 +254,14 @@ if (!Element.prototype.closest)
} while (el !== null && el.nodeType === 1); } while (el !== null && el.nodeType === 1);
}; };
if (!String.prototype.format)
String.prototype.format = function () {
var args = arguments;
return this.replace(/{(\d+)}/g, function (match, number) {
return typeof args[number] != 'undefined' ?
args[number] : match;
});
};
// https://stackoverflow.com/a/950146 // https://stackoverflow.com/a/950146
function import_js(url, cb) { function import_js(url, cb) {
@@ -316,7 +325,7 @@ function clmod(el, cls, add) {
var n2 = n1.replace(re, ' ') + (add ? ' ' + cls : ''); var n2 = n1.replace(re, ' ') + (add ? ' ' + cls : '');
if (!n1 == !n2) if (n1 == n2)
return false; return false;
el.className = n2; el.className = n2;
@@ -462,7 +471,7 @@ function linksplit(rp, id) {
q = '?' + q[1]; q = '?' + q[1];
} }
if (rp && rp.charAt(0) == '/') if (rp && rp[0] == '/')
rp = rp.slice(1); rp = rp.slice(1);
while (rp) { while (rp) {
@@ -1397,7 +1406,7 @@ function xhrchk(xhr, prefix, e404) {
return true; return true;
if (xhr.status == 403) if (xhr.status == 403)
return toast.err(0, prefix + "403, access denied\n\ntry pressing F5, maybe you got logged out"); return toast.err(0, prefix + (window.L && L.xhr403 || "403: access denied\n\ntry pressing F5, maybe you got logged out"));
if (xhr.status == 404) if (xhr.status == 404)
return toast.err(0, prefix + e404); return toast.err(0, prefix + e404);

View File

@@ -2,9 +2,9 @@ FROM alpine:3.15
WORKDIR /z WORKDIR /z
ENV ver_asmcrypto=5b994303a9d3e27e0915f72a10b6c2c51535a4dc \ ENV ver_asmcrypto=5b994303a9d3e27e0915f72a10b6c2c51535a4dc \
ver_hashwasm=4.9.0 \ ver_hashwasm=4.9.0 \
ver_marked=4.0.12 \ ver_marked=4.0.16 \
ver_mde=2.16.1 \ ver_mde=2.16.1 \
ver_codemirror=5.65.3 \ ver_codemirror=5.65.4 \
ver_fontawesome=5.13.0 \ ver_fontawesome=5.13.0 \
ver_zopfli=1.0.3 ver_zopfli=1.0.3
@@ -43,8 +43,6 @@ RUN mkdir -p /z/dist/no-pk \
# todo # todo
# https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/highlight.min.js
# https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/default.min.css
# https://prismjs.com/download.html#themes=prism-funky&languages=markup+css+clike+javascript+autohotkey+bash+basic+batch+c+csharp+cpp+cmake+diff+docker+go+ini+java+json+kotlin+latex+less+lisp+lua+makefile+objectivec+perl+powershell+python+r+jsx+ruby+rust+sass+scss+sql+swift+systemd+toml+typescript+vbnet+verilog+vhdl+yaml&plugins=line-highlight+line-numbers+autolinker # https://prismjs.com/download.html#themes=prism-funky&languages=markup+css+clike+javascript+autohotkey+bash+basic+batch+c+csharp+cpp+cmake+diff+docker+go+ini+java+json+kotlin+latex+less+lisp+lua+makefile+objectivec+perl+powershell+python+r+jsx+ruby+rust+sass+scss+sql+swift+systemd+toml+typescript+vbnet+verilog+vhdl+yaml&plugins=line-highlight+line-numbers+autolinker

View File

@@ -3,13 +3,13 @@ adds linetracking to marked.js v4.0.6;
add data-ln="%d" to most tags, %d is the source markdown line add data-ln="%d" to most tags, %d is the source markdown line
--- a/src/Lexer.js --- a/src/Lexer.js
+++ b/src/Lexer.js +++ b/src/Lexer.js
@@ -50,4 +50,5 @@ function mangle(text) { @@ -52,4 +52,5 @@ function mangle(text) {
export class Lexer { export class Lexer {
constructor(options) { constructor(options) {
+ this.ln = 1; // like most editors, start couting from 1 + this.ln = 1; // like most editors, start couting from 1
this.tokens = []; this.tokens = [];
this.tokens.links = Object.create(null); this.tokens.links = Object.create(null);
@@ -127,4 +128,15 @@ export class Lexer { @@ -128,4 +129,15 @@ export class Lexer {
} }
+ set_ln(token, ln = this.ln) { + set_ln(token, ln = this.ln) {
@@ -25,9 +25,9 @@ add data-ln="%d" to most tags, %d is the source markdown line
+ +
/** /**
* Lexing * Lexing
@@ -134,7 +146,11 @@ export class Lexer { @@ -140,7 +152,11 @@ export class Lexer {
src = src.replace(/^ +$/gm, '');
} }
- let token, lastToken, cutSrc, lastParagraphClipped; - let token, lastToken, cutSrc, lastParagraphClipped;
+ let token, lastToken, cutSrc, lastParagraphClipped, ln; + let token, lastToken, cutSrc, lastParagraphClipped, ln;
@@ -38,111 +38,112 @@ add data-ln="%d" to most tags, %d is the source markdown line
+ +
if (this.options.extensions if (this.options.extensions
&& this.options.extensions.block && this.options.extensions.block
@@ -142,4 +158,5 @@ export class Lexer { @@ -148,4 +164,5 @@ export class Lexer {
if (token = extTokenizer.call({ lexer: this }, src, tokens)) { if (token = extTokenizer.call({ lexer: this }, src, tokens)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.set_ln(token, ln); + this.set_ln(token, ln);
tokens.push(token); tokens.push(token);
return true; return true;
@@ -153,4 +170,5 @@ export class Lexer { @@ -159,4 +176,5 @@ export class Lexer {
if (token = this.tokenizer.space(src)) { if (token = this.tokenizer.space(src)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.set_ln(token, ln); // is \n if not type + this.set_ln(token, ln); // is \n if not type
if (token.type) { if (token.raw.length === 1 && tokens.length > 0) {
tokens.push(token); // if there's a single \n as a spacer, it's terminating the last line,
@@ -162,4 +180,5 @@ export class Lexer { @@ -172,4 +190,5 @@ export class Lexer {
if (token = this.tokenizer.code(src)) { if (token = this.tokenizer.code(src)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.set_ln(token, ln); + this.set_ln(token, ln);
lastToken = tokens[tokens.length - 1]; lastToken = tokens[tokens.length - 1];
// An indented code block cannot interrupt a paragraph. // An indented code block cannot interrupt a paragraph.
@@ -177,4 +196,5 @@ export class Lexer { @@ -187,4 +206,5 @@ export class Lexer {
if (token = this.tokenizer.fences(src)) { if (token = this.tokenizer.fences(src)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.set_ln(token, ln); + this.set_ln(token, ln);
tokens.push(token); tokens.push(token);
continue; continue;
@@ -184,4 +204,5 @@ export class Lexer { @@ -194,4 +214,5 @@ export class Lexer {
if (token = this.tokenizer.heading(src)) { if (token = this.tokenizer.heading(src)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.set_ln(token, ln); + this.set_ln(token, ln);
tokens.push(token); tokens.push(token);
continue; continue;
@@ -191,4 +212,5 @@ export class Lexer { @@ -201,4 +222,5 @@ export class Lexer {
if (token = this.tokenizer.hr(src)) { if (token = this.tokenizer.hr(src)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.set_ln(token, ln); + this.set_ln(token, ln);
tokens.push(token); tokens.push(token);
continue; continue;
@@ -198,4 +220,5 @@ export class Lexer { @@ -208,4 +230,5 @@ export class Lexer {
if (token = this.tokenizer.blockquote(src)) { if (token = this.tokenizer.blockquote(src)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.set_ln(token, ln); + this.set_ln(token, ln);
tokens.push(token); tokens.push(token);
continue; continue;
@@ -205,4 +228,5 @@ export class Lexer { @@ -215,4 +238,5 @@ export class Lexer {
if (token = this.tokenizer.list(src)) { if (token = this.tokenizer.list(src)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.set_ln(token, ln); + this.set_ln(token, ln);
tokens.push(token); tokens.push(token);
continue; continue;
@@ -212,4 +236,5 @@ export class Lexer { @@ -222,4 +246,5 @@ export class Lexer {
if (token = this.tokenizer.html(src)) { if (token = this.tokenizer.html(src)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.set_ln(token, ln); + this.set_ln(token, ln);
tokens.push(token); tokens.push(token);
continue; continue;
@@ -219,4 +244,5 @@ export class Lexer { @@ -229,4 +254,5 @@ export class Lexer {
if (token = this.tokenizer.def(src)) { if (token = this.tokenizer.def(src)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.set_ln(token, ln); + this.set_ln(token, ln);
lastToken = tokens[tokens.length - 1]; lastToken = tokens[tokens.length - 1];
if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) { if (lastToken && (lastToken.type === 'paragraph' || lastToken.type === 'text')) {
@@ -236,4 +262,5 @@ export class Lexer { @@ -246,4 +272,5 @@ export class Lexer {
if (token = this.tokenizer.table(src)) { if (token = this.tokenizer.table(src)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.set_ln(token, ln); + this.set_ln(token, ln);
tokens.push(token); tokens.push(token);
continue; continue;
@@ -243,4 +270,5 @@ export class Lexer { @@ -253,4 +280,5 @@ export class Lexer {
if (token = this.tokenizer.lheading(src)) { if (token = this.tokenizer.lheading(src)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.set_ln(token, ln); + this.set_ln(token, ln);
tokens.push(token); tokens.push(token);
continue; continue;
@@ -263,4 +291,5 @@ export class Lexer { @@ -273,4 +301,5 @@ export class Lexer {
} }
if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) { if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) {
+ this.set_ln(token, ln); + this.set_ln(token, ln);
lastToken = tokens[tokens.length - 1]; lastToken = tokens[tokens.length - 1];
if (lastParagraphClipped && lastToken.type === 'paragraph') { if (lastParagraphClipped && lastToken.type === 'paragraph') {
@@ -280,4 +309,6 @@ export class Lexer { @@ -290,4 +319,6 @@ export class Lexer {
if (token = this.tokenizer.text(src)) { if (token = this.tokenizer.text(src)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.set_ln(token, ln); + this.set_ln(token, ln);
+ this.ln++; + this.ln++;
lastToken = tokens[tokens.length - 1]; lastToken = tokens[tokens.length - 1];
if (lastToken && lastToken.type === 'text') { if (lastToken && lastToken.type === 'text') {
@@ -355,4 +386,5 @@ export class Lexer { @@ -365,4 +396,5 @@ export class Lexer {
if (token = extTokenizer.call({ lexer: this }, src, tokens)) { if (token = extTokenizer.call({ lexer: this }, src, tokens)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.ln = token.ln || this.ln; + this.ln = token.ln || this.ln;
tokens.push(token); tokens.push(token);
return true; return true;
@@ -420,4 +452,6 @@ export class Lexer { @@ -430,4 +462,6 @@ export class Lexer {
if (token = this.tokenizer.br(src)) { if (token = this.tokenizer.br(src)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ // no need to reset (no more blockTokens anyways) + // no need to reset (no more blockTokens anyways)
+ token.ln = this.ln++; + token.ln = this.ln++;
tokens.push(token); tokens.push(token);
continue; continue;
@@ -462,4 +496,5 @@ export class Lexer { @@ -472,4 +506,5 @@ export class Lexer {
if (token = this.tokenizer.inlineText(cutSrc, smartypants)) { if (token = this.tokenizer.inlineText(cutSrc, smartypants)) {
src = src.substring(token.raw.length); src = src.substring(token.raw.length);
+ this.ln = token.ln || this.ln; + this.ln = token.ln || this.ln;
if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started
prevChar = token.raw.slice(-1); prevChar = token.raw.slice(-1);
diff --git a/src/Parser.js b/src/Parser.js diff --git a/src/Parser.js b/src/Parser.js
index a22a2bc..884ad66 100644
--- a/src/Parser.js --- a/src/Parser.js
+++ b/src/Parser.js +++ b/src/Parser.js
@@ -18,4 +18,5 @@ export class Parser { @@ -18,4 +18,5 @@ export class Parser {
@@ -205,6 +206,7 @@ diff --git a/src/Parser.js b/src/Parser.js
// Run any renderer extensions // Run any renderer extensions
if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) { if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {
diff --git a/src/Renderer.js b/src/Renderer.js diff --git a/src/Renderer.js b/src/Renderer.js
index 7c36a75..aa1a53a 100644
--- a/src/Renderer.js --- a/src/Renderer.js
+++ b/src/Renderer.js +++ b/src/Renderer.js
@@ -11,6 +11,12 @@ export class Renderer { @@ -11,6 +11,12 @@ export class Renderer {
@@ -214,7 +216,7 @@ diff --git a/src/Renderer.js b/src/Renderer.js
} }
+ tag_ln(n) { + tag_ln(n) {
+ this.ln = ' data-ln="' + n + '"'; + this.ln = ` data-ln="${n}"`;
+ return this; + return this;
+ }; + };
+ +
@@ -233,65 +235,65 @@ diff --git a/src/Renderer.js b/src/Renderer.js
+ return '<pre' + this.ln + '><code class="' + return '<pre' + this.ln + '><code class="'
+ this.options.langPrefix + this.options.langPrefix
+ escape(lang, true) + escape(lang, true)
@@ -40,5 +46,5 @@ export class Renderer { @@ -43,5 +49,5 @@ export class Renderer {
*/
blockquote(quote) { blockquote(quote) {
- return '<blockquote>\n' + quote + '</blockquote>\n'; - return `<blockquote>\n${quote}</blockquote>\n`;
+ return '<blockquote' + this.ln + '>\n' + quote + '</blockquote>\n'; + return `<blockquote${this.ln}>\n${quote}</blockquote>\n`;
} }
@@ -51,4 +57,5 @@ export class Renderer { @@ -59,9 +65,9 @@ export class Renderer {
return '<h' if (this.options.headerIds) {
+ level const id = this.options.headerPrefix + slugger.slug(raw);
+ + this.ln - return `<h${level} id="${id}">${text}</h${level}>\n`;
+ ' id="' + return `<h${level}${this.ln} id="${id}">${text}</h${level}>\n`;
+ this.options.headerPrefix
@@ -61,5 +68,5 @@ export class Renderer {
} }
// ignore IDs // ignore IDs
- return '<h' + level + '>' + text + '</h' + level + '>\n'; - return `<h${level}>${text}</h${level}>\n`;
+ return '<h' + level + this.ln + '>' + text + '</h' + level + '>\n'; + return `<h${level}${this.ln}>${text}</h${level}>\n`;
} }
@@ -75,5 +82,5 @@ export class Renderer { @@ -80,5 +86,5 @@ export class Renderer {
*/
listitem(text) { listitem(text) {
- return '<li>' + text + '</li>\n'; - return `<li>${text}</li>\n`;
+ return '<li' + this.ln + '>' + text + '</li>\n'; + return `<li${this.ln}>${text}</li>\n`;
} }
@@ -87,5 +94,5 @@ export class Renderer { @@ -95,5 +101,5 @@ export class Renderer {
*/
paragraph(text) { paragraph(text) {
- return '<p>' + text + '</p>\n'; - return `<p>${text}</p>\n`;
+ return '<p' + this.ln + '>' + text + '</p>\n'; + return `<p${this.ln}>${text}</p>\n`;
} }
@@ -102,5 +109,5 @@ export class Renderer { @@ -117,5 +123,5 @@ export class Renderer {
*/
tablerow(content) { tablerow(content) {
- return '<tr>\n' + content + '</tr>\n'; - return `<tr>\n${content}</tr>\n`;
+ return '<tr' + this.ln + '>\n' + content + '</tr>\n'; + return `<tr${this.ln}>\n${content}</tr>\n`;
} }
@@ -127,5 +134,5 @@ export class Renderer { @@ -151,5 +157,5 @@ export class Renderer {
br() { br() {
- return this.options.xhtml ? '<br/>' : '<br>'; - return this.options.xhtml ? '<br/>' : '<br>';
+ return this.options.xhtml ? '<br' + this.ln + '/>' : '<br' + this.ln + '>'; + return this.options.xhtml ? `<br${this.ln}/>` : `<br${this.ln}>`;
} }
@@ -153,5 +160,5 @@ export class Renderer { @@ -190,5 +196,5 @@ export class Renderer {
} }
- let out = '<img src="' + href + '" alt="' + text + '"'; - let out = `<img src="${href}" alt="${text}"`;
+ let out = '<img' + this.ln + ' src="' + href + '" alt="' + text + '"'; + let out = `<img${this.ln} src="${href}" alt="${text}"`;
if (title) { if (title) {
out += ' title="' + title + '"'; out += ` title="${title}"`;
diff --git a/src/Tokenizer.js b/src/Tokenizer.js diff --git a/src/Tokenizer.js b/src/Tokenizer.js
index e8a69b6..2cc772b 100644
--- a/src/Tokenizer.js --- a/src/Tokenizer.js
+++ b/src/Tokenizer.js +++ b/src/Tokenizer.js
@@ -297,4 +297,7 @@ export class Tokenizer { @@ -302,4 +302,7 @@ export class Tokenizer {
const l = list.items.length; const l = list.items.length;
+ // each nested list gets +1 ahead; this hack makes every listgroup -1 but atleast it doesn't get infinitely bad + // each nested list gets +1 ahead; this hack makes every listgroup -1 but atleast it doesn't get infinitely bad

View File

@@ -14,6 +14,9 @@ help() { exec cat <<'EOF'
# #
# `gz` creates a gzip-compressed python sfx instead of bzip2 # `gz` creates a gzip-compressed python sfx instead of bzip2
# #
# `lang` limits which languages/translations to include,
# for example `lang eng` or `lang eng|nor`
#
# `no-cm` saves ~82k by removing easymde/codemirror # `no-cm` saves ~82k by removing easymde/codemirror
# (the fancy markdown editor) # (the fancy markdown editor)
# #
@@ -61,6 +64,7 @@ pybin=$(command -v python3 || command -v python) || {
exit 1 exit 1
} }
langs=
use_gz= use_gz=
zopf=2560 zopf=2560
while [ ! -z "$1" ]; do while [ ! -z "$1" ]; do
@@ -73,6 +77,7 @@ while [ ! -z "$1" ]; do
no-dd) no_dd=1 ; ;; no-dd) no_dd=1 ; ;;
no-cm) no_cm=1 ; ;; no-cm) no_cm=1 ; ;;
fast) zopf=100 ; ;; fast) zopf=100 ; ;;
lang) shift;langs="$1"; ;;
*) help ; ;; *) help ; ;;
esac esac
shift shift
@@ -262,6 +267,13 @@ rm have
tmv "$f" tmv "$f"
} }
[ $langs ] &&
for f in copyparty/web/{browser.js,splash.js}; do
gzip -d "$f.gz" || true
awk '/^\}/{l=0} !l; /^var Ls =/{l=1;next} o; /^\t["}]/{o=0} /^\t"'"$langs"'"/{o=1;print}' <$f >t
tmv "$f"
done
[ $repack ] || [ $repack ] ||
find | grep -E '\.py$' | find | grep -E '\.py$' |
grep -vE '__version__' | grep -vE '__version__' |

View File

@@ -71,6 +71,7 @@ copyparty/web/msg.css,
copyparty/web/msg.html, copyparty/web/msg.html,
copyparty/web/splash.css, copyparty/web/splash.css,
copyparty/web/splash.html, copyparty/web/splash.html,
copyparty/web/splash.js,
copyparty/web/ui.css, copyparty/web/ui.css,
copyparty/web/up2k.js, copyparty/web/up2k.js,
copyparty/web/util.js, copyparty/web/util.js,