Compare commits

...

27 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
ed
c110ccb9ae v1.2.10 2022-05-13 01:44:00 +02:00
ed
0143380306 help the query planner 2022-05-13 01:41:39 +02:00
30 changed files with 1544 additions and 699 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`
<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>
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>
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 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>
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
</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>

View File

@@ -42,6 +42,7 @@ import threading
import traceback
import http.client # py2: httplib
import urllib.parse
import calendar
from datetime import datetime
from urllib.parse import quote_from_bytes as quote
from urllib.parse import unquote_to_bytes as unquote
@@ -495,7 +496,7 @@ class Gateway(object):
ts = 60 * 60 * 24 * 2
try:
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:
info("bad HTML or OS [{}] [{}]".format(fdate, fsize))
# python cannot strptime(1959-01-01) on windows

View File

@@ -45,6 +45,7 @@ import threading
import traceback
import http.client # py2: httplib
import urllib.parse
import calendar
from datetime import datetime
from urllib.parse import quote_from_bytes as quote
from urllib.parse import unquote_to_bytes as unquote
@@ -443,7 +444,7 @@ class Gateway(object):
ts = 60 * 60 * 24 * 2
try:
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:
info("bad HTML or OS [{}] [{}]".format(fdate, fsize))
# python cannot strptime(1959-01-01) on windows

View File

@@ -8,7 +8,10 @@ import sqlite3
import argparse
DB_VER1 = 3
DB_VER2 = 4
DB_VER2 = 5
BY_PATH = None
NC = None
def die(msg):
@@ -57,8 +60,13 @@ def compare(n1, d1, n2, d2, verbose):
if rd.split("/", 1)[0] == ".hist":
continue
q = "select w from up where rd = ? and fn = ?"
hit = d2.execute(q, (rd, fn)).fetchone()
if BY_PATH:
q = "select w from up where rd = ? and fn = ?"
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:
miss += 1
if verbose:
@@ -70,27 +78,32 @@ def compare(n1, d1, n2, d2, verbose):
n = 0
miss = {}
nmiss = 0
for w1, k, v in d1.execute("select * from mt"):
for w1s, k, v in d1.execute("select * from mt"):
n += 1
if n % 100_000 == 0:
m = f"\033[36mchecked {n:,} of {nt:,} tags in {n1} against {n2}, so far {nmiss} missing tags\033[0m"
print(m)
q = "select rd, fn from up where substr(w,1,16) = ?"
rd, fn = d1.execute(q, (w1,)).fetchone()
q = "select w, rd, fn from up where substr(w,1,16) = ?"
w1, rd, fn = d1.execute(q, (w1s,)).fetchone()
if rd.split("/", 1)[0] == ".hist":
continue
q = "select substr(w,1,16) from up where rd = ? and fn = ?"
w2 = d2.execute(q, (rd, fn)).fetchone()
if BY_PATH:
q = "select w from up where rd = ? and fn = ?"
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:
w2 = w2[0]
v2 = None
if w2:
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()
if v2:
v2 = v2[0]
@@ -124,7 +137,7 @@ def compare(n1, d1, n2, d2, verbose):
for k, v in sorted(miss.items()):
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")
@@ -132,47 +145,75 @@ def compare(n1, d1, n2, d2, verbose):
def copy_mtp(d1, d2, tag, rm):
nt = next(d1.execute("select count(w) from mt where k = ?", (tag,)))[0]
n = 0
ndone = 0
for w1, k, v in d1.execute("select * from mt where k = ?", (tag,)):
ncopy = 0
nskip = 0
for w1s, k, v in d1.execute("select * from mt where k = ?", (tag,)):
n += 1
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)
q = "select rd, fn from up where substr(w,1,16) = ?"
rd, fn = d1.execute(q, (w1,)).fetchone()
q = "select w, rd, fn from up where substr(w,1,16) = ?"
w1, rd, fn = d1.execute(q, (w1s,)).fetchone()
if rd.split("/", 1)[0] == ".hist":
continue
q = "select substr(w,1,16) from up where rd = ? and fn = ?"
w2 = d2.execute(q, (rd, fn)).fetchone()
if BY_PATH:
q = "select w from up where rd = ? and fn = ?"
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:
continue
w2 = w2[0]
hit = d2.execute("select v from mt where w = ? and +k = ?", (w2, k)).fetchone()
w2s = w2[0][:16]
hit = d2.execute("select v from mt where w = ? and +k = ?", (w2s, k)).fetchone()
if hit:
hit = hit[0]
if hit != v:
ndone += 1
if hit is not None:
d2.execute("delete from mt where w = ? and +k = ?", (w2, k))
if NC and hit is not None:
nskip += 1
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:
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()
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():
global NC, BY_PATH
os.system("")
print()
ap = argparse.ArgumentParser()
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")
ap2 = ap.add_argument_group("informational / read-only stuff")
@@ -185,11 +226,29 @@ def main():
ap2.add_argument(
"-rm-mtp-flag",
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 = 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()
if ar.h2:
examples()
return
NC = ar.nc
BY_PATH = ar.by_path
for v in [ar.db, ar.src]:
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 = 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("--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("--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("--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 = ap.add_argument_group('debug options')

View File

@@ -1,8 +1,8 @@
# coding: utf-8
VERSION = (1, 2, 9)
CODENAME = "ftp btw"
BUILD_DT = (2022, 5, 12)
VERSION = (1, 3, 0)
CODENAME = "god dag"
BUILD_DT = (2022, 5, 22)
S_VERSION = ".".join(map(str, VERSION))
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):
rc = 404
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:
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
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)
self.reply(html.encode("utf-8"), status=rc)
@@ -2252,6 +2252,7 @@ class HttpCli(object):
"readme": readme,
"title": html_escape(self.vpath, crlf=True),
"srv_info": srv_info,
"lang": self.args.lang,
"dtheme": self.args.theme,
"themes": self.args.themes,
"turbolvl": self.args.turbo,
@@ -2413,7 +2414,7 @@ class HttpCli(object):
continue
w = r[0][:16]
q = "select k, v from mt where w = ? and k != 'x'"
q = "select k, v from mt where w = ? and +k != 'x'"
try:
for k, v in icur.execute(q, (w,)):
taglist[k] = True
@@ -2438,14 +2439,19 @@ class HttpCli(object):
if doc:
doc = unquotep(doc.replace("+", " ").split("?")[0])
j2a["docname"] = doc
doctxt = None
if next((x for x in files if x["name"] == doc), None):
with open(os.path.join(abspath, doc), "rb") as f:
doc = f.read().decode("utf-8", "replace")
docpath = os.path.join(abspath, doc)
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:
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:
j2a["no_prism"] = True

View File

@@ -3,7 +3,7 @@ from __future__ import print_function, unicode_literals
import time
import zlib
from datetime import datetime
import calendar
from .sutil import errdesc
from .util import yieldfile, sanitize_fn, spack, sunpack, min_ex
@@ -25,12 +25,12 @@ def dostime2unix(buf):
tf = "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}"
iso = tf.format(*tt)
dt = datetime.strptime(iso, "%Y-%m-%d %H:%M:%S")
return int(dt.timestamp())
dt = time.strptime(iso, "%Y-%m-%d %H:%M:%S")
return int(calendar.timegm(dt))
def unixtime2dos(ts):
tt = time.gmtime(ts)
tt = time.gmtime(ts + 1)
dy, dm, dd, th, tm, ts = list(tt)[:6]
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
ret = b"\x50\x4b\x03\x04" + req_ver
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"\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, "/", [])
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
ret += spack(b"<HH", len(bfn), z64_len)
ret += spack(b"<HH", len(bfn), 0x10 + z64_len)
if h_pos is not None:
# 2b comment, 2b diskno
ret += b"\x00" * 4
# 2b internal.attr, 4b external.attr
# infozip-macos: 0100 0000 a481 file:644
# infozip-macos: 0100 0100 0080 file:000
ret += b"\x01\x00\x00\x00\xa4\x81"
# infozip-macos: 0100 0000 a481 (spec-ver 1e03) file:644
# infozip-macos: 0100 0100 0080 (spec-ver 1e03) file:000
# 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
ret += spack(b"<L", min(h_pos, 0xFFFFFFFF))
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:
ret += spack(b"<HH" + b"Q" * len(z64v), 1, len(z64v) * 8, *z64v)
@@ -205,7 +216,7 @@ class StreamZip(object):
st = f["st"]
sz = st.st_size
ts = st.st_mtime + 1
ts = st.st_mtime
crc = None
if self.pre_crc:

View File

@@ -4,8 +4,8 @@ from __future__ import print_function, unicode_literals
import re
import os
import time
import calendar
import threading
from datetime import datetime
from operator import itemgetter
from .__init__ import ANYWIN, unicode
@@ -190,18 +190,17 @@ class U2idx(object):
if is_date:
is_date = False
v = v.upper().rstrip("Z").replace(",", " ").replace("T", " ")
while " " in v:
v = v.replace(" ", " ")
v = re.sub(r"[tzTZ, ]+", " ", v).strip()
for fmt in [
"%Y-%m-%d %H:%M:%S",
"%Y-%m-%d %H:%M",
"%Y-%m-%d %H",
"%Y-%m-%d",
"%Y-%m",
"%Y",
]:
try:
v = datetime.strptime(v, fmt).timestamp()
v = calendar.timegm(time.strptime(v, fmt))
break
except:
pass
@@ -328,7 +327,7 @@ class U2idx(object):
w = hit["w"]
del hit["w"]
tags = {}
q2 = "select k, v from mt where w = ? and k != 'x'"
q2 = "select k, v from mt where w = ? and +k != 'x'"
for k, v2 in cur.execute(q2, (w,)):
taglist[k] = True
tags[k] = v2

View File

@@ -949,7 +949,7 @@ class Up2k(object):
n_done += 1
for w in to_delete.keys():
q = "delete from mt where w = ? and k = 't:mtp'"
q = "delete from mt where w = ? and +k = 't:mtp'"
cur.execute(q, (w,))
to_delete = {}
@@ -987,7 +987,7 @@ class Up2k(object):
with self.mutex:
done = self._flush_mpool(wcur)
for w in done:
q = "delete from mt where w = ? and k = 't:mtp'"
q = "delete from mt where w = ? and +k = 't:mtp'"
cur.execute(q, (w,))
cur.connection.commit()
@@ -1115,7 +1115,7 @@ class Up2k(object):
for k in tags.keys():
q = "delete from mt where w = ? and ({})".format(
" or ".join(["k = ?"] * len(tags))
" or ".join(["+k = ?"] * len(tags))
)
args = [wark[:16]] + list(tags.keys())
write_cur.execute(q, tuple(args))

View File

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

View File

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

View File

@@ -68,7 +68,7 @@
<div id="op_cfg" class="opview opbox opwide"></div>
<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 %}
<a href="/{{ n[0] }}">{{ n[1] }}</a>
{%- endfor %}
@@ -120,7 +120,7 @@
<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>
@@ -138,6 +138,7 @@
themes = {{ themes }},
dtheme = "{{ dtheme }}",
srvinf = "{{ srv_info }}",
lang = "{{ lang }}",
def_hcols = {{ def_hcols|tojson }},
have_up2k_idx = {{ have_up2k_idx|tojson }},
have_tags_idx = {{ have_tags_idx|tojson }},

File diff suppressed because it is too large Load Diff

View File

@@ -304,7 +304,7 @@ blink {
}
html.z #toc {
background: #282828;
border-top: 1px solid #2c2c2c;
@@ -428,7 +428,7 @@ blink {
}
html.z .mdo a {
color: #000;
}

View File

@@ -135,7 +135,7 @@ var md_opt = {
};
(function () {
var l = localStorage,
var l = localStorage,
drk = l.light != 1,
btn = document.getElementById("lightswitch"),
f = function (e) {
@@ -143,14 +143,14 @@ if (e) { e.preventDefault(); drk = !drk; }
document.documentElement.className = drk? "z":"y";
btn.innerHTML = "go " + (drk ? "light":"dark");
l.light = drk? 0:1;
};
};
btn.onclick = f;
f();
})();
</script>
<script src="/.cpr/util.js?_={{ ts }}"></script>
<script src="/.cpr/util.js?_={{ ts }}"></script>
<script src="/.cpr/deps/marked.js?_={{ ts }}"></script>
<script src="/.cpr/md.js?_={{ ts }}"></script>
{%- if edit %}

View File

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

View File

@@ -45,7 +45,7 @@ l.light = drk? 0:1;
})();
</script>
<script src="/.cpr/util.js?_={{ ts }}"></script>
<script src="/.cpr/util.js?_={{ ts }}"></script>
<script src="/.cpr/deps/marked.js?_={{ ts }}"></script>
<script src="/.cpr/deps/easymde.js?_={{ ts }}"></script>
<script src="/.cpr/mde.js?_={{ ts }}"></script>

View File

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

View File

@@ -2,49 +2,49 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{ svcname }}</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
<meta charset="utf-8">
<title>{{ svcname }}</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
{{ html_head }}
<link rel="stylesheet" media="screen" href="/.cpr/msg.css?_={{ ts }}">
<link rel="stylesheet" media="screen" href="/.cpr/msg.css?_={{ ts }}">
</head>
<body>
<div id="box">
{%- if h1 %}
<h1>{{ h1 }}</h1>
{%- endif %}
{%- if h2 %}
<h2>{{ h2 }}</h2>
{%- endif %}
{%- if p %}
<p>{{ p }}</p>
{%- endif %}
<div id="box">
{%- if pre %}
<pre>{{ pre }}</pre>
{%- endif %}
{%- if h1 %}
<h1>{{ h1 }}</h1>
{%- endif %}
{%- if html %}
{{ html }}
{%- endif %}
{%- if h2 %}
<h2>{{ h2 }}</h2>
{%- endif %}
{%- if click %}
<script>document.getElementsByTagName("a")[0].click()</script>
{%- endif %}
</div>
{%- if p %}
<p>{{ p }}</p>
{%- endif %}
{%- if redir %}
<script>
setTimeout(function() {
window.location.replace("{{ redir }}");
}, 1000);
</script>
{%- endif %}
{%- if pre %}
<pre>{{ pre }}</pre>
{%- endif %}
{%- if html %}
{{ html }}
{%- endif %}
{%- if click %}
<script>document.getElementsByTagName("a")[0].click()</script>
{%- endif %}
</div>
{%- if redir %}
<script>
setTimeout(function() {
window.location.replace("{{ redir }}");
}, 1000);
</script>
{%- endif %}
</body>
</html>

View File

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

View File

@@ -2,110 +2,106 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{ svcname }}</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
<meta charset="utf-8">
<title>{{ svcname }}</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
{{ html_head }}
<link rel="stylesheet" media="screen" href="/.cpr/splash.css?_={{ ts }}">
<link rel="stylesheet" media="screen" href="/.cpr/ui.css?_={{ ts }}">
<link rel="stylesheet" media="screen" href="/.cpr/splash.css?_={{ ts }}">
<link rel="stylesheet" media="screen" href="/.cpr/ui.css?_={{ ts }}">
</head>
<body>
<div id="wrap">
<a href="/?h" class="refresh">refresh</a>
<div id="wrap">
<a id="a" href="/?h" class="refresh">refresh</a>
{%- if this.uname == '*' %}
<p>howdy stranger &nbsp; <small>(you're not logged in)</small></p>
{%- else %}
<a href="/?pw=x" class="logout">logout</a>
<p>welcome back, <strong>{{ this.uname }}</strong></p>
{%- endif %}
{%- if this.uname == '*' %}
<p id="b">howdy stranger &nbsp; <small>(you're not logged in)</small></p>
{%- else %}
<a id="c" href="/?pw=x" class="logout">logout</a>
<p><span id="m">welcome back,</span> <strong>{{ this.uname }}</strong></p>
{%- endif %}
{%- if msg %}
<div id="msg">
{{ msg }}
</div>
{%- endif %}
{%- if msg %}
<div id="msg">
{{ msg }}
</div>
{%- endif %}
{%- if avol %}
<h1>admin panel:</h1>
<table><tr><td> <!-- hehehe -->
<table class="num">
<tr><td>scanning</td><td>{{ scanning }}</td></tr>
<tr><td>hash-q</td><td>{{ hashq }}</td></tr>
<tr><td>tag-q</td><td>{{ tagq }}</td></tr>
<tr><td>mtp-q</td><td>{{ mtpq }}</td></tr>
</table>
</td><td>
<table class="vols">
<thead><tr><th>vol</th><th>action</th><th>status</th></tr></thead>
<tbody>
{% for mp in avol %}
{%- 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>
{%- endif %}
{% endfor %}
</tbody>
</table>
</td></tr></table>
<div class="btns">
<a 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>
</div>
{%- endif %}
{%- if avol %}
<h1>admin panel:</h1>
<table><tr><td> <!-- hehehe -->
<table class="num">
<tr><td>scanning</td><td>{{ scanning }}</td></tr>
<tr><td>hash-q</td><td>{{ hashq }}</td></tr>
<tr><td>tag-q</td><td>{{ tagq }}</td></tr>
<tr><td>mtp-q</td><td>{{ mtpq }}</td></tr>
</table>
</td><td>
<table class="vols">
<thead><tr><th>vol</th><th id="t">action</th><th>status</th></tr></thead>
<tbody>
{% for mp in avol %}
{%- if mp in vstate and vstate[mp] %}
<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 %}
{% endfor %}
</tbody>
</table>
</td></tr></table>
<div class="btns">
<a id="d" href="/?stack" tt="shows the state of all active threads">dump stack</a>
<a id="e" href="/?reload=cfg" tt="reload config files (accounts/volumes/volflags),$Nand rescan all e2ds volumes">reload cfg</a>
</div>
{%- endif %}
{%- if rvol %}
<h1>you can browse these:</h1>
<ul>
{% for mp in rvol %}
<li><a href="{{ mp }}{{ url_suf }}">{{ mp }}</a></li>
{% endfor %}
</ul>
{%- endif %}
{%- if rvol %}
<h1 id="f">you can browse:</h1>
<ul>
{% for mp in rvol %}
<li><a href="{{ mp }}{{ url_suf }}">{{ mp }}</a></li>
{% endfor %}
</ul>
{%- endif %}
{%- if wvol %}
<h1>you can upload to:</h1>
<ul>
{% for mp in wvol %}
<li><a href="{{ mp }}{{ url_suf }}">{{ mp }}</a></li>
{% endfor %}
</ul>
{%- endif %}
{%- if wvol %}
<h1 id="g">you can upload to:</h1>
<ul>
{% for mp in wvol %}
<li><a href="{{ mp }}{{ url_suf }}">{{ mp }}</a></li>
{% endfor %}
</ul>
{%- endif %}
<h1 id="cc">client config:</h1>
<ul>
{% if k304 %}
<li><a href="/?k304=n">disable k304</a> (currently enabled)
{%- else %}
<li><a href="/?k304=y" class="r">enable k304</a> (currently disabled)
{% 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>
<li><a href="/?reset" class="r" onclick="localStorage.clear();return true">reset client settings</a></li>
</ul>
<h1 id="cc">client config:</h1>
<ul>
{% if k304 %}
<li><a id="h" href="/?k304=n">disable k304</a> (currently enabled)
{%- else %}
<li><a id="i" href="/?k304=y" class="r">enable k304</a> (currently disabled)
{% endif %}
<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 id="k" href="/?reset" class="r" onclick="localStorage.clear();return true">reset client settings</a></li>
</ul>
<h1>login for more:</h1>
<ul>
<form method="post" enctype="multipart/form-data" action="/{{ qvpath }}">
<input type="hidden" name="act" value="login" />
<input type="password" name="cppwd" />
<input type="submit" value="Login" />
</form>
</ul>
</div>
<h1 id="l">login for more:</h1>
<ul>
<form method="post" enctype="multipart/form-data" action="/{{ qvpath }}">
<input type="hidden" name="act" value="login" />
<input type="password" name="cppwd" />
<input type="submit" value="Login" />
</form>
</ul>
</div>
<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 src="/.cpr/util.js?_={{ ts }}"></script>
<script>
tt.init();
{%- if this.uname == '*' %}
QS('input[name="cppwd"]').focus();
{%- endif %}
</script>
<script src="/.cpr/splash.js?_={{ ts }}"></script>
</body>
</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;
padding-right: .7em;
}
#toast.r #toastb {
text-align: right;
}
#toast pre {
margin: 0;
}
@@ -163,15 +166,23 @@ html.y #tt {
background: #fff;
border-color: #888 #000 #777 #000;
}
html.bz #tt {
background: #202231;
border-color: #3b3f58;
}
html.y #tt,
html.y #toast {
box-shadow: 0 .3em 1em rgba(0,0,0,0.4);
}
#modalc code,
html.y #tt code {
background: #060;
color: #fff;
}
#modalc code {
color: #060;
background: transparent;
border: 1px solid #ccc;
}
html.y #tt em {
color: #d38;
}
@@ -478,10 +489,20 @@ html.y textarea:focus {
border-top: .4em solid #b80;
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 {
background: #444;
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 th {
border-color: #444;

View File

@@ -568,12 +568,12 @@ function Donut(uc, st) {
function fsearch_explain(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))
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 = '';
}
var suggest_up2k = 'this is the basic uploader; <a href="#" id="u2yea">up2k</a> is better';
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';
var suggest_up2k = L.u_su2k;
function got_deps() {
return subtle || window.asmCrypto || window.hashwasm;
@@ -608,15 +601,18 @@ function up2k_init(subtle) {
var loading_deps = false;
function init_deps() {
if (!loading_deps && !got_deps()) {
var fn = 'sha512.' + sha_js + '.js';
showmodal('<h1>loading ' + fn + '</h1><h2>since ' + shame + '</h2><h4>thanks chrome</h4>');
var fn = 'sha512.' + sha_js + '.js',
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);
if (is_https)
ebi('u2foot').innerHTML = shame + ' so <em>this</em> uploader will do like 500 KiB/s at best';
else
ebi('u2foot').innerHTML = 'seems like ' + shame + ' so do that if you want more performance <span style="color:#' +
(sha_js == 'ac' ? 'c84">(expecting 20' : '8a5">(but dont worry too much, expect 100') + ' MiB/s)</span>';
if (is_https) {
// chrome<37 firefox<34 edge<12 opera<24 safari<7
m = L.u_ancient;
setmsg('');
}
ebi('u2foot').innerHTML = '<big>' + m + '</big>';
}
loading_deps = true;
}
@@ -648,16 +644,6 @@ function up2k_init(subtle) {
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'),
uc = {},
fdom_ctr = 0,
@@ -715,7 +701,7 @@ function up2k_init(subtle) {
bobslice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
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;
apply_flag_cfg();
@@ -737,14 +723,14 @@ function up2k_init(subtle) {
var mup, up = QS('#up_zd');
var msr, sr = QS('#srch_zd');
if (!has(perms, 'write'))
mup = 'you do not have write-access to this folder';
mup = L.u_ewrite;
if (!has(perms, 'read'))
msr = 'you do not have read-access to this folder';
msr = L.u_eread;
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';
sr.querySelector('span').textContent = msr || 'drop it here';
up.querySelector('span').textContent = mup || L.udt_drop;
sr.querySelector('span').textContent = msr || L.udt_drop;
clmod(up, 'err', mup);
clmod(sr, 'err', msr);
clmod(up, 'ok', !mup);
@@ -970,22 +956,22 @@ function up2k_init(subtle) {
function gotallfiles(good_files, nil_files, bad_files) {
var ntot = good_files.concat(nil_files, 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++)
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 () {
gotallfiles(good_files, nil_files, []);
});
}
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++)
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 () {
gotallfiles(good_files.concat(nil_files), [], []);
}, function () {
@@ -999,12 +985,15 @@ function up2k_init(subtle) {
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++)
msg.push('<li>' + esc(good_files[a][1]) + '</li>');
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);
}
@@ -1056,7 +1045,7 @@ function up2k_init(subtle) {
pvis.addfile([
uc.fsearch ? esc(entry.name) : linksplit(
entry.purl + uricom_enc(entry.name)).join(' '),
'📐 hash',
'📐 ' + L.u_hashing,
''
], fobj.size, draw_each);
@@ -1104,11 +1093,11 @@ function up2k_init(subtle) {
}
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)
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())
nhash = 0;
@@ -1129,7 +1118,7 @@ function up2k_init(subtle) {
}
if ((nhash || nsend) && !uc.fsearch) {
if (!st.bytes.finished) {
ebi('u2etat').innerHTML = '(preparing to upload)';
ebi('u2etat').innerHTML = L.u_etaprep;
}
else {
st.time.busy += td;
@@ -1142,7 +1131,7 @@ function up2k_init(subtle) {
eta = Math.floor(rem / bps);
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;
}
@@ -1260,24 +1249,21 @@ function up2k_init(subtle) {
donut.on(is_busy);
if (!is_busy) {
var k = uc.fsearch ? 'searches' : 'uploads',
ks = uc.fsearch ? 'Search' : 'Upload',
tok = uc.fsearch ? 'successful (found on server)' : 'completed successfully',
tng = uc.fsearch ? 'failed (NOT found on server)' : 'failed, sorry',
var sr = uc.fsearch,
ok = pvis.ctr["ok"],
ng = pvis.ctr["ng"],
t = uc.ask_up ? 0 : 10;
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)
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)
toast.ok(t, '{0} {1}'.format(ks, tok));
toast.ok(t, sr ? L.ur_1so : L.ur_1uo);
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)
toast.err(t, '{0} {1}'.format(ks, tng));
toast.err(t, sr ? L.ur_1sn : L.ur_1un);
timer.rm(etafun);
timer.rm(donut.do);
@@ -1530,7 +1516,7 @@ function up2k_init(subtle) {
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');
apop(st.busy.hash, t);
st.todo.handshake.push(t);
@@ -1658,8 +1644,8 @@ function up2k_init(subtle) {
if (!response || !response.hits || !response.hits.length) {
smsg = '404';
msg = ('not found on server <a href="#" onclick="fsearch_explain(' +
(has(perms, 'write') ? '0' : '1') + ')" class="fsearch_explain">(explain)</a>');
msg = (L.u_s404 + ' <a href="#" onclick="fsearch_explain(' +
(has(perms, 'write') ? '0' : '1') + ')" class="fsearch_explain">(' + L.u_expl + ')</a>');
}
else {
smsg = 'found';
@@ -1734,7 +1720,7 @@ function up2k_init(subtle) {
'npart': t.postlist[a]
});
msg = 'uploading';
msg = L.u_upping;
done = false;
if (sort)
@@ -1811,8 +1797,8 @@ function up2k_init(subtle) {
tasker();
return;
}
err = t.t_uploading ? "finalize upload" : t.srch ? "perform search" : "initiate upload";
xhrchk(xhr, "server rejected the request to " + err + ";\n\nfile: " + t.name + "\n\nerror ", "404, target folder not found");
err = t.t_uploading ? L.u_ehsfin : t.srch ? L.u_ehssrch : L.u_ehsinit;
xhrchk(xhr, err + ";\n\nfile: " + t.name + "\n\nerror ", "404, target folder not found");
}
}
xhr.onload = function (e) {
@@ -1871,7 +1857,7 @@ function up2k_init(subtle) {
console.log("ignoring dupe-segment error", t);
}
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);
}
@@ -1900,7 +1886,7 @@ function up2k_init(subtle) {
return;
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);
orz2(xhr);
@@ -2013,10 +1999,8 @@ function up2k_init(subtle) {
}
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>',
msgs = '<p class="warn">WARNING: turbo enabled, <span>&nbsp;search results can be incorrect; see turbo-button tooltip</span></p>',
msg = uc.fsearch ? msgs : msgu,
omsg = uc.fsearch ? msgu : msgs,
var msg = uc.fsearch ? L.u_ts : L.u_tu,
omsg = uc.fsearch ? L.u_tu : L.u_ts,
html = ebi('u2foot').innerHTML,
ohtml = html;
@@ -2026,7 +2010,7 @@ function up2k_init(subtle) {
if (msg && html.indexOf(msg) === -1)
html = html.replace(omsg, '') + msg;
else if (!msg)
html = html.replace(msgu, '').replace(msgs, '');
html = html.replace(L.u_tu, '').replace(L.u_ts, '');
if (html !== ohtml)
ebi('u2foot').innerHTML = html;
@@ -2062,7 +2046,7 @@ function up2k_init(subtle) {
try {
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);
ebi('u2bm').innerHTML = ico + '&nbsp; <sup>' + desc + '</sup>';

View File

@@ -7,6 +7,7 @@ if (!window['console'])
var is_touch = 'ontouchstart' in window,
is_https = (window.location + '').indexOf('https:') === 0,
IPHONE = is_touch && /iPhone|iPad|iPod/i.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);
};
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
function import_js(url, cb) {
@@ -316,7 +325,7 @@ function clmod(el, cls, add) {
var n2 = n1.replace(re, ' ') + (add ? ' ' + cls : '');
if (!n1 == !n2)
if (n1 == n2)
return false;
el.className = n2;
@@ -462,7 +471,7 @@ function linksplit(rp, id) {
q = '?' + q[1];
}
if (rp && rp.charAt(0) == '/')
if (rp && rp[0] == '/')
rp = rp.slice(1);
while (rp) {
@@ -1397,7 +1406,7 @@ function xhrchk(xhr, prefix, e404) {
return true;
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)
return toast.err(0, prefix + e404);

View File

@@ -2,9 +2,9 @@ FROM alpine:3.15
WORKDIR /z
ENV ver_asmcrypto=5b994303a9d3e27e0915f72a10b6c2c51535a4dc \
ver_hashwasm=4.9.0 \
ver_marked=4.0.12 \
ver_marked=4.0.16 \
ver_mde=2.16.1 \
ver_codemirror=5.65.3 \
ver_codemirror=5.65.4 \
ver_fontawesome=5.13.0 \
ver_zopfli=1.0.3
@@ -43,8 +43,6 @@ RUN mkdir -p /z/dist/no-pk \
# 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

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
--- a/src/Lexer.js
+++ b/src/Lexer.js
@@ -50,4 +50,5 @@ function mangle(text) {
@@ -52,4 +52,5 @@ function mangle(text) {
export class Lexer {
constructor(options) {
+ this.ln = 1; // like most editors, start couting from 1
this.tokens = [];
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) {
@@ -25,9 +25,9 @@ add data-ln="%d" to most tags, %d is the source markdown line
+
/**
* Lexing
@@ -134,7 +146,11 @@ export class Lexer {
src = src.replace(/^ +$/gm, '');
@@ -140,7 +152,11 @@ export class Lexer {
}
- let token, lastToken, cutSrc, lastParagraphClipped;
+ 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
&& 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)) {
src = src.substring(token.raw.length);
+ this.set_ln(token, ln);
tokens.push(token);
return true;
@@ -153,4 +170,5 @@ export class Lexer {
@@ -159,4 +176,5 @@ export class Lexer {
if (token = this.tokenizer.space(src)) {
src = src.substring(token.raw.length);
+ this.set_ln(token, ln); // is \n if not type
if (token.type) {
tokens.push(token);
@@ -162,4 +180,5 @@ export class Lexer {
if (token.raw.length === 1 && tokens.length > 0) {
// if there's a single \n as a spacer, it's terminating the last line,
@@ -172,4 +190,5 @@ export class Lexer {
if (token = this.tokenizer.code(src)) {
src = src.substring(token.raw.length);
+ this.set_ln(token, ln);
lastToken = tokens[tokens.length - 1];
// 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)) {
src = src.substring(token.raw.length);
+ this.set_ln(token, ln);
tokens.push(token);
continue;
@@ -184,4 +204,5 @@ export class Lexer {
@@ -194,4 +214,5 @@ export class Lexer {
if (token = this.tokenizer.heading(src)) {
src = src.substring(token.raw.length);
+ this.set_ln(token, ln);
tokens.push(token);
continue;
@@ -191,4 +212,5 @@ export class Lexer {
@@ -201,4 +222,5 @@ export class Lexer {
if (token = this.tokenizer.hr(src)) {
src = src.substring(token.raw.length);
+ this.set_ln(token, ln);
tokens.push(token);
continue;
@@ -198,4 +220,5 @@ export class Lexer {
@@ -208,4 +230,5 @@ export class Lexer {
if (token = this.tokenizer.blockquote(src)) {
src = src.substring(token.raw.length);
+ this.set_ln(token, ln);
tokens.push(token);
continue;
@@ -205,4 +228,5 @@ export class Lexer {
@@ -215,4 +238,5 @@ export class Lexer {
if (token = this.tokenizer.list(src)) {
src = src.substring(token.raw.length);
+ this.set_ln(token, ln);
tokens.push(token);
continue;
@@ -212,4 +236,5 @@ export class Lexer {
@@ -222,4 +246,5 @@ export class Lexer {
if (token = this.tokenizer.html(src)) {
src = src.substring(token.raw.length);
+ this.set_ln(token, ln);
tokens.push(token);
continue;
@@ -219,4 +244,5 @@ export class Lexer {
@@ -229,4 +254,5 @@ export class Lexer {
if (token = this.tokenizer.def(src)) {
src = src.substring(token.raw.length);
+ this.set_ln(token, ln);
lastToken = tokens[tokens.length - 1];
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)) {
src = src.substring(token.raw.length);
+ this.set_ln(token, ln);
tokens.push(token);
continue;
@@ -243,4 +270,5 @@ export class Lexer {
@@ -253,4 +280,5 @@ export class Lexer {
if (token = this.tokenizer.lheading(src)) {
src = src.substring(token.raw.length);
+ this.set_ln(token, ln);
tokens.push(token);
continue;
@@ -263,4 +291,5 @@ export class Lexer {
@@ -273,4 +301,5 @@ export class Lexer {
}
if (this.state.top && (token = this.tokenizer.paragraph(cutSrc))) {
+ this.set_ln(token, ln);
lastToken = tokens[tokens.length - 1];
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)) {
src = src.substring(token.raw.length);
+ this.set_ln(token, ln);
+ this.ln++;
lastToken = tokens[tokens.length - 1];
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)) {
src = src.substring(token.raw.length);
+ this.ln = token.ln || this.ln;
tokens.push(token);
return true;
@@ -420,4 +452,6 @@ export class Lexer {
@@ -430,4 +462,6 @@ export class Lexer {
if (token = this.tokenizer.br(src)) {
src = src.substring(token.raw.length);
+ // no need to reset (no more blockTokens anyways)
+ token.ln = this.ln++;
tokens.push(token);
continue;
@@ -462,4 +496,5 @@ export class Lexer {
@@ -472,4 +506,5 @@ export class Lexer {
if (token = this.tokenizer.inlineText(cutSrc, smartypants)) {
src = src.substring(token.raw.length);
+ this.ln = token.ln || this.ln;
if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started
prevChar = token.raw.slice(-1);
diff --git a/src/Parser.js b/src/Parser.js
index a22a2bc..884ad66 100644
--- a/src/Parser.js
+++ b/src/Parser.js
@@ -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
if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {
diff --git a/src/Renderer.js b/src/Renderer.js
index 7c36a75..aa1a53a 100644
--- a/src/Renderer.js
+++ b/src/Renderer.js
@@ -11,6 +11,12 @@ export class Renderer {
@@ -214,10 +216,10 @@ diff --git a/src/Renderer.js b/src/Renderer.js
}
+ tag_ln(n) {
+ this.ln = ' data-ln="' + n + '"';
+ this.ln = ` data-ln="${n}"`;
+ return this;
+ };
+
+
code(code, infostring, escaped) {
const lang = (infostring || '').match(/\S*/)[0];
@@ -26,10 +32,10 @@ export class Renderer {
@@ -233,65 +235,65 @@ diff --git a/src/Renderer.js b/src/Renderer.js
+ return '<pre' + this.ln + '><code class="'
+ this.options.langPrefix
+ escape(lang, true)
@@ -40,5 +46,5 @@ export class Renderer {
@@ -43,5 +49,5 @@ export class Renderer {
*/
blockquote(quote) {
- return '<blockquote>\n' + quote + '</blockquote>\n';
+ return '<blockquote' + this.ln + '>\n' + quote + '</blockquote>\n';
- return `<blockquote>\n${quote}</blockquote>\n`;
+ return `<blockquote${this.ln}>\n${quote}</blockquote>\n`;
}
@@ -51,4 +57,5 @@ export class Renderer {
return '<h'
+ level
+ + this.ln
+ ' id="'
+ this.options.headerPrefix
@@ -61,5 +68,5 @@ export class Renderer {
@@ -59,9 +65,9 @@ export class Renderer {
if (this.options.headerIds) {
const id = this.options.headerPrefix + slugger.slug(raw);
- return `<h${level} id="${id}">${text}</h${level}>\n`;
+ return `<h${level}${this.ln} id="${id}">${text}</h${level}>\n`;
}
// ignore IDs
- return '<h' + level + '>' + text + '</h' + level + '>\n';
+ return '<h' + level + this.ln + '>' + text + '</h' + level + '>\n';
- return `<h${level}>${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) {
- return '<li>' + text + '</li>\n';
+ return '<li' + this.ln + '>' + text + '</li>\n';
- return `<li>${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) {
- return '<p>' + text + '</p>\n';
+ return '<p' + this.ln + '>' + text + '</p>\n';
- return `<p>${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) {
- return '<tr>\n' + content + '</tr>\n';
+ return '<tr' + this.ln + '>\n' + content + '</tr>\n';
- return `<tr>\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() {
- 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' + this.ln + ' src="' + href + '" alt="' + text + '"';
- let out = `<img src="${href}" alt="${text}"`;
+ let out = `<img${this.ln} src="${href}" alt="${text}"`;
if (title) {
out += ' title="' + title + '"';
out += ` title="${title}"`;
diff --git a/src/Tokenizer.js b/src/Tokenizer.js
index e8a69b6..2cc772b 100644
--- a/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;
+ // 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
#
# `lang` limits which languages/translations to include,
# for example `lang eng` or `lang eng|nor`
#
# `no-cm` saves ~82k by removing easymde/codemirror
# (the fancy markdown editor)
#
@@ -61,6 +64,7 @@ pybin=$(command -v python3 || command -v python) || {
exit 1
}
langs=
use_gz=
zopf=2560
while [ ! -z "$1" ]; do
@@ -73,6 +77,7 @@ while [ ! -z "$1" ]; do
no-dd) no_dd=1 ; ;;
no-cm) no_cm=1 ; ;;
fast) zopf=100 ; ;;
lang) shift;langs="$1"; ;;
*) help ; ;;
esac
shift
@@ -262,6 +267,13 @@ rm have
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 ] ||
find | grep -E '\.py$' |
grep -vE '__version__' |

View File

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