mirror of
https://github.com/9001/copyparty.git
synced 2025-11-04 22:03:21 +00:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
053ac74734 | ||
|
|
cced99fafa | ||
|
|
a009ff53f7 | ||
|
|
ca16c4108d | ||
|
|
d1b6c67dc3 | ||
|
|
a61f8133d5 | ||
|
|
38d797a544 | ||
|
|
16c1877f50 | ||
|
|
da5f15a778 | ||
|
|
396c64ecf7 | ||
|
|
252c3a7985 | ||
|
|
a3ecbf0ae7 | ||
|
|
314327d8f2 | ||
|
|
bfacd06929 | ||
|
|
4f5e8f8cf5 | ||
|
|
1fbb4c09cc | ||
|
|
b332e1992b | ||
|
|
5955940b82 | ||
|
|
231a03bcfd | ||
|
|
bc85723657 | ||
|
|
be32b743c6 | ||
|
|
83c9843059 | ||
|
|
11cf43626d | ||
|
|
a6dc5e2ce3 | ||
|
|
38593a0394 | ||
|
|
95309afeea | ||
|
|
c2bf6fe2a3 | ||
|
|
99ac324fbd | ||
|
|
5562de330f | ||
|
|
95014236ac | ||
|
|
6aa7386138 | ||
|
|
3226a1f588 | ||
|
|
b4cf890cd8 | ||
|
|
ce09e323af | ||
|
|
941aedb177 | ||
|
|
87a0d502a3 | ||
|
|
cab7c1b0b8 | ||
|
|
d5892341b6 | ||
|
|
646557a43e | ||
|
|
ed8d34ab43 | ||
|
|
5e34463c77 | ||
|
|
1b14eb7959 | ||
|
|
ed48c2d0ed | ||
|
|
26fe84b660 | ||
|
|
5938230270 | ||
|
|
1a33a047fa | ||
|
|
43a8bcefb9 | ||
|
|
2e740e513f | ||
|
|
8a21a86b61 | ||
|
|
f600116205 | ||
|
|
1c03705de8 | ||
|
|
f7e461fac6 | ||
|
|
03ce6c97ff | ||
|
|
ffd9e76e07 | ||
|
|
fc49cb1e67 | ||
|
|
f5712d9f25 |
190
README.md
190
README.md
@@ -8,21 +8,21 @@
|
|||||||
|
|
||||||
turn your phone or raspi into a portable file server with resumable uploads/downloads using *any* web browser
|
turn your phone or raspi into a portable file server with resumable uploads/downloads using *any* web browser
|
||||||
|
|
||||||
* server runs on anything with `py2.7` or `py3.3+`
|
* server only needs `py2.7` or `py3.3+`, all dependencies optional
|
||||||
* browse/upload with IE4 / netscape4.0 on win3.11 (heh)
|
* browse/upload with IE4 / netscape4.0 on win3.11 (heh)
|
||||||
* *resumable* uploads need `firefox 34+` / `chrome 41+` / `safari 7+` for full speed
|
* *resumable* uploads need `firefox 34+` / `chrome 41+` / `safari 7+` for full speed
|
||||||
* code standard: `black`
|
* code standard: `black`
|
||||||
|
|
||||||
📷 **screenshots:** [browser](#the-browser) // [upload](#uploading) // [thumbnails](#thumbnails) // [md-viewer](#markdown-viewer) // [search](#searching) // [fsearch](#file-search) // [zip-DL](#zip-downloads) // [ie4](#browser-support)
|
📷 **screenshots:** [browser](#the-browser) // [upload](#uploading) // [unpost](#unpost) // [thumbnails](#thumbnails) // [search](#searching) // [fsearch](#file-search) // [zip-DL](#zip-downloads) // [md-viewer](#markdown-viewer) // [ie4](#browser-support)
|
||||||
|
|
||||||
|
|
||||||
## readme toc
|
## readme toc
|
||||||
|
|
||||||
* top
|
* top
|
||||||
* [quickstart](#quickstart) - download [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py) and you're all set!
|
* **[quickstart](#quickstart)** - download **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py)** and you're all set!
|
||||||
* [on debian](#on-debian) - recommended additional steps on debian
|
* [on debian](#on-debian) - recommended additional steps on debian
|
||||||
* [notes](#notes) - general notes
|
* [notes](#notes) - general notes
|
||||||
* [status](#status) - summary: all planned features work! now please enjoy the bloatening
|
* [status](#status) - feature summary
|
||||||
* [testimonials](#testimonials) - small collection of user feedback
|
* [testimonials](#testimonials) - small collection of user feedback
|
||||||
* [bugs](#bugs)
|
* [bugs](#bugs)
|
||||||
* [general bugs](#general-bugs)
|
* [general bugs](#general-bugs)
|
||||||
@@ -30,30 +30,31 @@ turn your phone or raspi into a portable file server with resumable uploads/down
|
|||||||
* [accounts and volumes](#accounts-and-volumes) - per-folder, per-user permissions
|
* [accounts and volumes](#accounts-and-volumes) - per-folder, per-user permissions
|
||||||
* [the browser](#the-browser) - accessing a copyparty server using a web-browser
|
* [the browser](#the-browser) - accessing a copyparty server using a web-browser
|
||||||
* [tabs](#tabs) - the main tabs in the ui
|
* [tabs](#tabs) - the main tabs in the ui
|
||||||
* [hotkeys](#hotkeys) - the browser has the following hotkeys (always qwerty)
|
* [hotkeys](#hotkeys) - the browser has the following hotkeys
|
||||||
* [navpane](#navpane) - switching between breadcrumbs or navpane
|
* [navpane](#navpane) - switching between breadcrumbs or navpane
|
||||||
* [thumbnails](#thumbnails) - press `g` to toggle image/video thumbnails instead of the file listing
|
* [thumbnails](#thumbnails) - press `g` to toggle grid-view instead of the file listing
|
||||||
* [zip downloads](#zip-downloads) - download folders (or file selections) as `zip` or `tar` files
|
* [zip downloads](#zip-downloads) - download folders (or file selections) as `zip` or `tar` files
|
||||||
* [uploading](#uploading) - web-browsers can upload using `bup` and `up2k`
|
* [uploading](#uploading) - web-browsers can upload using `bup` and `up2k`
|
||||||
* [file-search](#file-search) - drop files/folders into up2k to see if they exist on the server
|
* [file-search](#file-search) - drop files/folders into up2k to see if they exist on the server
|
||||||
* [unpost](#unpost) - undo/delete accidental uploads
|
* [unpost](#unpost) - undo/delete accidental uploads
|
||||||
* [file manager](#file-manager) - cut/paste, rename, and delete files/folders (if you have permission)
|
* [file manager](#file-manager) - cut/paste, rename, and delete files/folders (if you have permission)
|
||||||
* [batch rename](#batch-rename) - select some files and press F2 to bring up the rename UI
|
* [batch rename](#batch-rename) - select some files and press `F2` to bring up the rename UI
|
||||||
* [markdown viewer](#markdown-viewer) - and there are *two* editors
|
* [markdown viewer](#markdown-viewer) - and there are *two* editors
|
||||||
* [other tricks](#other-tricks)
|
* [other tricks](#other-tricks)
|
||||||
* [searching](#searching) - search by size, date, path/name, mp3-tags, ...
|
* [searching](#searching) - search by size, date, path/name, mp3-tags, ...
|
||||||
* [server config](#server-config)
|
* [server config](#server-config)
|
||||||
* [file indexing](#file-indexing)
|
* [file indexing](#file-indexing)
|
||||||
* [upload rules](#upload-rules) - set upload rules using volume flags, some examples
|
* [upload rules](#upload-rules) - set upload rules using volume flags
|
||||||
* [compress uploads](#compress-uploads) - files can be autocompressed on upload
|
* [compress uploads](#compress-uploads) - files can be autocompressed on upload
|
||||||
* [database location](#database-location) - can be stored in-volume (default) or elsewhere
|
* [database location](#database-location) - in-volume (`.hist/up2k.db`, default) or somewhere else
|
||||||
* [metadata from audio files](#metadata-from-audio-files) - set `-e2t` to index tags on upload
|
* [metadata from audio files](#metadata-from-audio-files) - set `-e2t` to index tags on upload
|
||||||
* [file parser plugins](#file-parser-plugins) - provide custom parsers to index additional tags
|
* [file parser plugins](#file-parser-plugins) - provide custom parsers to index additional tags
|
||||||
* [complete examples](#complete-examples)
|
* [complete examples](#complete-examples)
|
||||||
* [browser support](#browser-support) - TLDR: yes
|
* [browser support](#browser-support) - TLDR: yes
|
||||||
* [client examples](#client-examples) - interact with copyparty using non-browser clients
|
* [client examples](#client-examples) - interact with copyparty using non-browser clients
|
||||||
* [up2k](#up2k) - quick outline of the up2k protocol, see [uploading](#uploading) for the web-client
|
* [up2k](#up2k) - quick outline of the up2k protocol, see [uploading](#uploading) for the web-client
|
||||||
* [performance](#performance) - defaults are good for most cases
|
* [why chunk-hashes](#why-chunk-hashes) - a single sha512 would be better, right?
|
||||||
|
* [performance](#performance) - defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload
|
||||||
* [dependencies](#dependencies) - mandatory deps
|
* [dependencies](#dependencies) - mandatory deps
|
||||||
* [optional dependencies](#optional-dependencies) - install these to enable bonus features
|
* [optional dependencies](#optional-dependencies) - install these to enable bonus features
|
||||||
* [install recommended deps](#install-recommended-deps)
|
* [install recommended deps](#install-recommended-deps)
|
||||||
@@ -71,12 +72,12 @@ turn your phone or raspi into a portable file server with resumable uploads/down
|
|||||||
|
|
||||||
## quickstart
|
## quickstart
|
||||||
|
|
||||||
download [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py) and you're all set!
|
download **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py)** and you're all set!
|
||||||
|
|
||||||
running the sfx without arguments (for example doubleclicking it on Windows) will give everyone full access to the current folder; see `-h` for help if you want [accounts and volumes](#accounts-and-volumes) etc
|
running the sfx without arguments (for example doubleclicking it on Windows) will give everyone read/write access to the current folder; see `-h` for help if you want [accounts and volumes](#accounts-and-volumes) etc
|
||||||
|
|
||||||
some recommended options:
|
some recommended options:
|
||||||
* `-e2dsa` enables general file indexing, see [search configuration](#search-configuration)
|
* `-e2dsa` enables general [file indexing](#file-indexing)
|
||||||
* `-e2ts` enables audio metadata indexing (needs either FFprobe or Mutagen), see [optional dependencies](#optional-dependencies)
|
* `-e2ts` enables audio metadata indexing (needs either FFprobe or Mutagen), see [optional dependencies](#optional-dependencies)
|
||||||
* `-v /mnt/music:/music:r:rw,foo -a foo:bar` shares `/mnt/music` as `/music`, `r`eadable by anyone, and read-write for user `foo`, password `bar`
|
* `-v /mnt/music:/music:r:rw,foo -a foo:bar` shares `/mnt/music` as `/music`, `r`eadable by anyone, and read-write for user `foo`, password `bar`
|
||||||
* replace `:r:rw,foo` with `:r,foo` to only make the folder readable by `foo` and nobody else
|
* replace `:r:rw,foo` with `:r,foo` to only make the folder readable by `foo` and nobody else
|
||||||
@@ -90,9 +91,7 @@ you may also want these, especially on servers:
|
|||||||
|
|
||||||
### on debian
|
### on debian
|
||||||
|
|
||||||
recommended additional steps on debian
|
recommended additional steps on debian which enable audio metadata and thumbnails (from images and videos):
|
||||||
|
|
||||||
enable audio metadata and thumbnails (from images and videos):
|
|
||||||
|
|
||||||
* as root, run the following:
|
* as root, run the following:
|
||||||
`apt install python3 python3-pip python3-dev ffmpeg`
|
`apt install python3 python3-pip python3-dev ffmpeg`
|
||||||
@@ -114,12 +113,12 @@ browser-specific:
|
|||||||
* Android-Chrome: increase "parallel uploads" for higher speed (android bug)
|
* Android-Chrome: increase "parallel uploads" for higher speed (android bug)
|
||||||
* Android-Firefox: takes a while to select files (their fix for ☝️)
|
* Android-Firefox: takes a while to select files (their fix for ☝️)
|
||||||
* Desktop-Firefox: ~~may use gigabytes of RAM if your files are massive~~ *seems to be OK now*
|
* Desktop-Firefox: ~~may use gigabytes of RAM if your files are massive~~ *seems to be OK now*
|
||||||
* Desktop-Firefox: may stop you from deleting folders you've uploaded until you visit `about:memory` and click `Minimize memory usage`
|
* Desktop-Firefox: may stop you from deleting files you've uploaded until you visit `about:memory` and click `Minimize memory usage`
|
||||||
|
|
||||||
|
|
||||||
## status
|
## status
|
||||||
|
|
||||||
summary: all planned features work! now please enjoy the bloatening
|
feature summary
|
||||||
|
|
||||||
* backend stuff
|
* backend stuff
|
||||||
* ☑ sanic multipart parser
|
* ☑ sanic multipart parser
|
||||||
@@ -137,7 +136,7 @@ summary: all planned features work! now please enjoy the bloatening
|
|||||||
* ☑ [folders as zip / tar files](#zip-downloads)
|
* ☑ [folders as zip / tar files](#zip-downloads)
|
||||||
* ☑ FUSE client (read-only)
|
* ☑ FUSE client (read-only)
|
||||||
* browser
|
* browser
|
||||||
* ☑ navpane (directory tree sidebar)
|
* ☑ [navpane](#navpane) (directory tree sidebar)
|
||||||
* ☑ file manager (cut/paste, delete, [batch-rename](#batch-rename))
|
* ☑ file manager (cut/paste, delete, [batch-rename](#batch-rename))
|
||||||
* ☑ audio player (with OS media controls)
|
* ☑ audio player (with OS media controls)
|
||||||
* ☑ image gallery with webm player
|
* ☑ image gallery with webm player
|
||||||
@@ -168,12 +167,11 @@ small collection of user feedback
|
|||||||
* Windows: python 3.7 and older cannot read tags with FFprobe, so use Mutagen or upgrade
|
* Windows: python 3.7 and older cannot read tags with FFprobe, so use Mutagen or upgrade
|
||||||
* Windows: python 2.7 cannot index non-ascii filenames with `-e2d`
|
* Windows: python 2.7 cannot index non-ascii filenames with `-e2d`
|
||||||
* Windows: python 2.7 cannot handle filenames with mojibake
|
* Windows: python 2.7 cannot handle filenames with mojibake
|
||||||
* `--th-ff-jpg` may fix video thumbnails on some FFmpeg versions
|
* `--th-ff-jpg` may fix video thumbnails on some FFmpeg versions (macos, some linux)
|
||||||
|
|
||||||
## general bugs
|
## general bugs
|
||||||
|
|
||||||
* all volumes must exist / be available on startup; up2k (mtp especially) gets funky otherwise
|
* all volumes must exist / be available on startup; up2k (mtp especially) gets funky otherwise
|
||||||
* cannot mount something at `/d1/d2/d3` unless `d2` exists inside `d1`
|
|
||||||
* probably more, pls let me know
|
* probably more, pls let me know
|
||||||
|
|
||||||
## not my bugs
|
## not my bugs
|
||||||
@@ -194,15 +192,16 @@ per-folder, per-user permissions
|
|||||||
* `-a usr:pwd` adds account `usr` with password `pwd`
|
* `-a usr:pwd` adds account `usr` with password `pwd`
|
||||||
* `-v .::r` adds current-folder `.` as the webroot, `r`eadable by anyone
|
* `-v .::r` adds current-folder `.` as the webroot, `r`eadable by anyone
|
||||||
* the syntax is `-v src:dst:perm:perm:...` so local-path, url-path, and one or more permissions to set
|
* the syntax is `-v src:dst:perm:perm:...` so local-path, url-path, and one or more permissions to set
|
||||||
* when granting permissions to an account, the names are comma-separated: `-v .::r,usr1,usr2:rw,usr3,usr4`
|
* granting the same permissions to multiple accounts:
|
||||||
|
`-v .::r,usr1,usr2:rw,usr3,usr4` = usr1/2 read-only, 3/4 read-write
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
* `r` (read): browse folder contents, download files, download as zip/tar
|
* `r` (read): browse folder contents, download files, download as zip/tar
|
||||||
* `w` (write): upload files, move files *into* folder
|
* `w` (write): upload files, move files *into* this folder
|
||||||
* `m` (move): move files/folders *from* folder
|
* `m` (move): move files/folders *from* this folder
|
||||||
* `d` (delete): delete files/folders
|
* `d` (delete): delete files/folders
|
||||||
|
|
||||||
example:
|
examples:
|
||||||
* add accounts named u1, u2, u3 with passwords p1, p2, p3: `-a u1:p1 -a u2:p2 -a u3:p3`
|
* add accounts named u1, u2, u3 with passwords p1, p2, p3: `-a u1:p1 -a u2:p2 -a u3:p3`
|
||||||
* make folder `/srv` the root of the filesystem, read-only by anyone: `-v /srv::r`
|
* make folder `/srv` the root of the filesystem, read-only by anyone: `-v /srv::r`
|
||||||
* make folder `/mnt/music` available at `/music`, read-only for u1 and u2, read-write for u3: `-v /mnt/music:music:r,u1,u2:rw,u3`
|
* make folder `/mnt/music` available at `/music`, read-only for u1 and u2, read-write for u3: `-v /mnt/music:music:r,u1,u2:rw,u3`
|
||||||
@@ -236,14 +235,14 @@ the main tabs in the ui
|
|||||||
## hotkeys
|
## hotkeys
|
||||||
|
|
||||||
the browser has the following hotkeys (always qwerty)
|
the browser has the following hotkeys (always qwerty)
|
||||||
* `B` toggle breadcrumbs / navpane
|
* `B` toggle breadcrumbs / [navpane](#navpane)
|
||||||
* `I/K` prev/next folder
|
* `I/K` prev/next folder
|
||||||
* `M` parent folder (or unexpand current)
|
* `M` parent folder (or unexpand current)
|
||||||
* `G` toggle list / grid view
|
* `G` toggle list / [grid view](#thumbnails)
|
||||||
* `T` toggle thumbnails / icons
|
* `T` toggle thumbnails / icons
|
||||||
* `ctrl-X` cut selected files/folders
|
* `ctrl-X` cut selected files/folders
|
||||||
* `ctrl-V` paste
|
* `ctrl-V` paste
|
||||||
* `F2` rename selected file/folder
|
* `F2` [rename](#batch-rename) selected file/folder
|
||||||
* when a file/folder is selected (in not-grid-view):
|
* when a file/folder is selected (in not-grid-view):
|
||||||
* `Up/Down` move cursor
|
* `Up/Down` move cursor
|
||||||
* shift+`Up/Down` select and move cursor
|
* shift+`Up/Down` select and move cursor
|
||||||
@@ -270,7 +269,7 @@ the browser has the following hotkeys (always qwerty)
|
|||||||
* `M` mute
|
* `M` mute
|
||||||
* when the navpane is open:
|
* when the navpane is open:
|
||||||
* `A/D` adjust tree width
|
* `A/D` adjust tree width
|
||||||
* in the grid view:
|
* in the [grid view](#thumbnails):
|
||||||
* `S` toggle multiselect
|
* `S` toggle multiselect
|
||||||
* shift+`A/D` zoom
|
* shift+`A/D` zoom
|
||||||
* in the markdown editor:
|
* in the markdown editor:
|
||||||
@@ -288,12 +287,14 @@ switching between breadcrumbs or navpane
|
|||||||
|
|
||||||
click the `🌲` or pressing the `B` hotkey to toggle between breadcrumbs path (default), or a navpane (tree-browser sidebar thing)
|
click the `🌲` or pressing the `B` hotkey to toggle between breadcrumbs path (default), or a navpane (tree-browser sidebar thing)
|
||||||
|
|
||||||
click `[-]` and `[+]` (or hotkeys `A`/`D`) to adjust the size, and the `[a]` toggles if the tree should widen dynamically as you go deeper or stay fixed-size
|
* `[-]` and `[+]` (or hotkeys `A`/`D`) adjust the size
|
||||||
|
* `[v]` jumps to the currently open folder
|
||||||
|
* `[a]` toggles automatic widening as you go deeper
|
||||||
|
|
||||||
|
|
||||||
## thumbnails
|
## thumbnails
|
||||||
|
|
||||||
press `g` to toggle image/video thumbnails instead of the file listing
|
press `g` to toggle grid-view instead of the file listing, and `t` toggles icons / thumbnails
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -308,7 +309,7 @@ in the grid/thumbnail view, if the audio player panel is open, songs will start
|
|||||||
|
|
||||||
download folders (or file selections) as `zip` or `tar` files
|
download folders (or file selections) as `zip` or `tar` files
|
||||||
|
|
||||||
select which type of archive you want in the browser settings tab:
|
select which type of archive you want in the `[⚙️] config` tab:
|
||||||
|
|
||||||
| name | url-suffix | description |
|
| name | url-suffix | description |
|
||||||
|--|--|--|
|
|--|--|--|
|
||||||
@@ -355,18 +356,22 @@ see [up2k](#up2k) for details on how it works
|
|||||||
the up2k UI is the epitome of polished inutitive experiences:
|
the up2k UI is the epitome of polished inutitive experiences:
|
||||||
* "parallel uploads" specifies how many chunks to upload at the same time
|
* "parallel uploads" specifies how many chunks to upload at the same time
|
||||||
* `[🏃]` analysis of other files should continue while one is uploading
|
* `[🏃]` analysis of other files should continue while one is uploading
|
||||||
* `[💭]` ask for confirmation before files are added to the list
|
* `[💭]` ask for confirmation before files are added to the queue
|
||||||
* `[💤]` sync uploading between other copyparty browser-tabs so only one is active
|
* `[💤]` sync uploading between other copyparty browser-tabs so only one is active
|
||||||
* `[🔎]` switch between upload and file-search mode
|
* `[🔎]` switch between upload and [file-search](#file-search) mode
|
||||||
|
|
||||||
and then theres the tabs below it,
|
and then theres the tabs below it,
|
||||||
* `[ok]` is uploads which completed successfully
|
* `[ok]` is the files which completed successfully
|
||||||
* `[ng]` is the uploads which failed / got rejected (already exists, ...)
|
* `[ng]` is the ones that failed / got rejected (already exists, ...)
|
||||||
* `[done]` shows a combined list of `[ok]` and `[ng]`, chronological order
|
* `[done]` shows a combined list of `[ok]` and `[ng]`, chronological order
|
||||||
* `[busy]` files which are currently hashing, pending-upload, or uploading
|
* `[busy]` files which are currently hashing, pending-upload, or uploading
|
||||||
* plus up to 3 entries each from `[done]` and `[que]` for context
|
* plus up to 3 entries each from `[done]` and `[que]` for context
|
||||||
* `[que]` is all the files that are still queued
|
* `[que]` is all the files that are still queued
|
||||||
|
|
||||||
|
note that since up2k has to read each file twice, `[🎈 bup]` can be up to 2x faster in some extreme cases (huge files combined with internet connection faster than the read-speed of your HDD)
|
||||||
|
|
||||||
|
if you are resuming a massive upload and want to skip hashing the files which already finished, you can enable `turbo` in the `[⚙️] config` tab, but please read the tooltip on that button
|
||||||
|
|
||||||
|
|
||||||
### file-search
|
### file-search
|
||||||
|
|
||||||
@@ -381,10 +386,6 @@ files go into `[ok]` if they exist (and you get a link to where it is), otherwis
|
|||||||
|
|
||||||
adding the same file multiple times is blocked, so if you first search for a file and then decide to upload it, you have to click the `[cleanup]` button to discard `[done]` files (or just refresh the page)
|
adding the same file multiple times is blocked, so if you first search for a file and then decide to upload it, you have to click the `[cleanup]` button to discard `[done]` files (or just refresh the page)
|
||||||
|
|
||||||
note that since up2k has to read the file twice, `[🎈 bup]` can be up to 2x faster in extreme cases (if your internet connection is faster than the read-speed of your HDD)
|
|
||||||
|
|
||||||
up2k has saved a few uploads from becoming corrupted in-transfer already; caught an android phone on wifi redhanded in wireshark with a bitflip, however bup with https would *probably* have noticed as well (thanks to tls also functioning as an integrity check)
|
|
||||||
|
|
||||||
|
|
||||||
### unpost
|
### unpost
|
||||||
|
|
||||||
@@ -399,12 +400,22 @@ you can unpost even if you don't have regular move/delete access, however only f
|
|||||||
|
|
||||||
cut/paste, rename, and delete files/folders (if you have permission)
|
cut/paste, rename, and delete files/folders (if you have permission)
|
||||||
|
|
||||||
|
file selection: click somewhere on the line (not the link itsef), then:
|
||||||
|
* `space` to toggle
|
||||||
|
* `up/down` to move
|
||||||
|
* `shift-up/down` to move-and-select
|
||||||
|
* `ctrl-shift-up/down` to also scroll
|
||||||
|
|
||||||
|
* cut: select some files and `ctrl-x`
|
||||||
|
* paste: `ctrl-v` in another folder
|
||||||
|
* rename: `F2`
|
||||||
|
|
||||||
you can move files across browser tabs (cut in one tab, paste in another)
|
you can move files across browser tabs (cut in one tab, paste in another)
|
||||||
|
|
||||||
|
|
||||||
## batch rename
|
## batch rename
|
||||||
|
|
||||||
select some files and press F2 to bring up the rename UI
|
select some files and press `F2` to bring up the rename UI
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -464,6 +475,12 @@ and there are *two* editors
|
|||||||
|
|
||||||
* if you are using media hotkeys to switch songs and are getting tired of seeing the OSD popup which Windows doesn't let you disable, consider https://ocv.me/dev/?media-osd-bgone.ps1
|
* if you are using media hotkeys to switch songs and are getting tired of seeing the OSD popup which Windows doesn't let you disable, consider https://ocv.me/dev/?media-osd-bgone.ps1
|
||||||
|
|
||||||
|
* click the bottom-left `π` to open a javascript prompt for debugging
|
||||||
|
|
||||||
|
* files named `.prologue.html` / `.epilogue.html` will be rendered before/after directory listings unless `--no-logues`
|
||||||
|
|
||||||
|
* files named `README.md` / `readme.md` will be rendered after directory listings unless `--no-readme` (but `.epilogue.html` takes precedence)
|
||||||
|
|
||||||
|
|
||||||
## searching
|
## searching
|
||||||
|
|
||||||
@@ -486,15 +503,15 @@ add the argument `-e2ts` to also scan/index tags from music files, which brings
|
|||||||
|
|
||||||
## file indexing
|
## file indexing
|
||||||
|
|
||||||
file indexing relies on two databases, the up2k filetree (`-e2d`) and the metadata tags (`-e2t`). Configuration can be done through arguments, volume flags, or a mix of both.
|
file indexing relies on two database tables, the up2k filetree (`-e2d`) and the metadata tags (`-e2t`), stored in `.hist/up2k.db`. Configuration can be done through arguments, volume flags, or a mix of both.
|
||||||
|
|
||||||
through arguments:
|
through arguments:
|
||||||
* `-e2d` enables file indexing on upload
|
* `-e2d` enables file indexing on upload
|
||||||
* `-e2ds` scans writable folders for new files on startup
|
* `-e2ds` also scans writable folders for new files on startup
|
||||||
* `-e2dsa` scans all mounted volumes (including readonly ones)
|
* `-e2dsa` also scans all mounted volumes (including readonly ones)
|
||||||
* `-e2t` enables metadata indexing on upload
|
* `-e2t` enables metadata indexing on upload
|
||||||
* `-e2ts` scans for tags in all files that don't have tags yet
|
* `-e2ts` also scans for tags in all files that don't have tags yet
|
||||||
* `-e2tsr` deletes all existing tags, does a full reindex
|
* `-e2tsr` also deletes all existing tags, doing a full reindex
|
||||||
|
|
||||||
the same arguments can be set as volume flags, in addition to `d2d` and `d2t` for disabling:
|
the same arguments can be set as volume flags, in addition to `d2d` and `d2t` for disabling:
|
||||||
* `-v ~/music::r:c,e2dsa:c,e2tsr` does a full reindex of everything on startup
|
* `-v ~/music::r:c,e2dsa:c,e2tsr` does a full reindex of everything on startup
|
||||||
@@ -502,10 +519,11 @@ the same arguments can be set as volume flags, in addition to `d2d` and `d2t` fo
|
|||||||
* `-v ~/music::r:c,d2t` disables all `-e2t*` (tags), does not affect `-e2d*`
|
* `-v ~/music::r:c,d2t` disables all `-e2t*` (tags), does not affect `-e2d*`
|
||||||
|
|
||||||
note:
|
note:
|
||||||
|
* the parser currently can't handle `c,e2dsa,e2tsr` so you have to `c,e2dsa:c,e2tsr`
|
||||||
* `e2tsr` is probably always overkill, since `e2ds`/`e2dsa` would pick up any file modifications and `e2ts` would then reindex those, unless there is a new copyparty version with new parsers and the release note says otherwise
|
* `e2tsr` is probably always overkill, since `e2ds`/`e2dsa` would pick up any file modifications and `e2ts` would then reindex those, unless there is a new copyparty version with new parsers and the release note says otherwise
|
||||||
* the rescan button in the admin panel has no effect unless the volume has `-e2ds` or higher
|
* the rescan button in the admin panel has no effect unless the volume has `-e2ds` or higher
|
||||||
|
|
||||||
you can choose to only index filename/path/size/last-modified (and not the hash of the file contents) by setting `--no-hash` or the volume-flag `:c,dhash`, this has the following consequences:
|
to save some time, you can choose to only index filename/path/size/last-modified (and not the hash of the file contents) by setting `--no-hash` or the volume-flag `:c,dhash`, this has the following consequences:
|
||||||
* initial indexing is way faster, especially when the volume is on a network disk
|
* initial indexing is way faster, especially when the volume is on a network disk
|
||||||
* makes it impossible to [file-search](#file-search)
|
* makes it impossible to [file-search](#file-search)
|
||||||
* if someone uploads the same file contents, the upload will not be detected as a dupe, so it will not get symlinked or rejected
|
* if someone uploads the same file contents, the upload will not be detected as a dupe, so it will not get symlinked or rejected
|
||||||
@@ -534,9 +552,7 @@ you can also set transaction limits which apply per-IP and per-volume, but these
|
|||||||
|
|
||||||
## compress uploads
|
## compress uploads
|
||||||
|
|
||||||
files can be autocompressed on upload
|
files can be autocompressed on upload, either on user-request (if config allows) or forced by server-config
|
||||||
|
|
||||||
compression is either on user-request (if config allows) or forced by server-config
|
|
||||||
|
|
||||||
* volume flag `gz` allows gz compression
|
* volume flag `gz` allows gz compression
|
||||||
* volume flag `xz` allows lzma compression
|
* volume flag `xz` allows lzma compression
|
||||||
@@ -557,7 +573,7 @@ some examples,
|
|||||||
|
|
||||||
## database location
|
## database location
|
||||||
|
|
||||||
can be stored in-volume (default) or elsewhere
|
in-volume (`.hist/up2k.db`, default) or somewhere else
|
||||||
|
|
||||||
copyparty creates a subfolder named `.hist` inside each volume where it stores the database, thumbnails, and some other stuff
|
copyparty creates a subfolder named `.hist` inside each volume where it stores the database, thumbnails, and some other stuff
|
||||||
|
|
||||||
@@ -579,7 +595,7 @@ set `-e2t` to index tags on upload
|
|||||||
|
|
||||||
if you add/remove a tag from `mte` you will need to run with `-e2tsr` once to rebuild the database, otherwise only new files will be affected
|
if you add/remove a tag from `mte` you will need to run with `-e2tsr` once to rebuild the database, otherwise only new files will be affected
|
||||||
|
|
||||||
but instead of using `-mte`, `-mth` is a better way to hide tags in the browser: these tags will not be displayed by default, but they still get indexed and become searchable, and users can choose to unhide them in the settings pane
|
but instead of using `-mte`, `-mth` is a better way to hide tags in the browser: these tags will not be displayed by default, but they still get indexed and become searchable, and users can choose to unhide them in the `[⚙️] config` pane
|
||||||
|
|
||||||
`-mtm` can be used to add or redefine a metadata mapping, say you have media files with `foo` and `bar` tags and you want them to display as `qux` in the browser (preferring `foo` if both are present), then do `-mtm qux=foo,bar` and now you can `-mte artist,title,qux`
|
`-mtm` can be used to add or redefine a metadata mapping, say you have media files with `foo` and `bar` tags and you want them to display as `qux` in the browser (preferring `foo` if both are present), then do `-mtm qux=foo,bar` and now you can `-mte artist,title,qux`
|
||||||
|
|
||||||
@@ -647,10 +663,10 @@ TLDR: yes
|
|||||||
| **= feature =** | ie6 | ie9 | ie10 | ie11 | ff 52 | c 49 | iOS | Andr |
|
| **= feature =** | ie6 | ie9 | ie10 | ie11 | ff 52 | c 49 | iOS | Andr |
|
||||||
|
|
||||||
* internet explorer 6 to 8 behave the same
|
* internet explorer 6 to 8 behave the same
|
||||||
* firefox 52 and chrome 49 are the last winxp versions
|
* firefox 52 and chrome 49 are the final winxp versions
|
||||||
* `*1` yes, but extremely slow (ie10: 1 MiB/s, ie11: 270 KiB/s)
|
* `*1` yes, but extremely slow (ie10: `1 MiB/s`, ie11: `270 KiB/s`)
|
||||||
* `*2` causes a full-page refresh on each navigation
|
* `*2` causes a full-page refresh on each navigation
|
||||||
* `*3` using a wasm decoder which can sometimes get stuck and consumes a bit more power
|
* `*3` using a wasm decoder which consumes a bit more power
|
||||||
|
|
||||||
quick summary of more eccentric web-browsers trying to view a directory index:
|
quick summary of more eccentric web-browsers trying to view a directory index:
|
||||||
|
|
||||||
@@ -694,6 +710,8 @@ copyparty returns a truncated sha512sum of your PUT/POST as base64; you can gene
|
|||||||
b512(){ printf "$((sha512sum||shasum -a512)|sed -E 's/ .*//;s/(..)/\\x\1/g')"|base64|tr '+/' '-_'|head -c44;}
|
b512(){ printf "$((sha512sum||shasum -a512)|sed -E 's/ .*//;s/(..)/\\x\1/g')"|base64|tr '+/' '-_'|head -c44;}
|
||||||
b512 <movie.mkv
|
b512 <movie.mkv
|
||||||
|
|
||||||
|
you can provide passwords using cookie 'cppwd=hunter2', as a url query `?pw=hunter2`, or with basic-authentication (either as the username or password)
|
||||||
|
|
||||||
|
|
||||||
# up2k
|
# up2k
|
||||||
|
|
||||||
@@ -710,12 +728,25 @@ quick outline of the up2k protocol, see [uploading](#uploading) for the web-clie
|
|||||||
* server writes chunks into place based on the hash
|
* server writes chunks into place based on the hash
|
||||||
* client does another handshake with the hashlist; server replies with OK or a list of chunks to reupload
|
* client does another handshake with the hashlist; server replies with OK or a list of chunks to reupload
|
||||||
|
|
||||||
|
up2k has saved a few uploads from becoming corrupted in-transfer already; caught an android phone on wifi redhanded in wireshark with a bitflip, however bup with https would *probably* have noticed as well (thanks to tls also functioning as an integrity check)
|
||||||
|
|
||||||
|
|
||||||
|
## why chunk-hashes
|
||||||
|
|
||||||
|
a single sha512 would be better, right?
|
||||||
|
|
||||||
|
this is due to `crypto.subtle` not providing a streaming api (or the option to seed the sha512 hasher with a starting hash)
|
||||||
|
|
||||||
|
as a result, the hashes are much less useful than they could have been (search the server by sha512, provide the sha512 in the response http headers, ...)
|
||||||
|
|
||||||
|
hashwasm would solve the streaming issue but reduces hashing speed for sha512 (xxh128 does 6 GiB/s), and it would make old browsers and [iphones](https://bugs.webkit.org/show_bug.cgi?id=228552) unsupported
|
||||||
|
|
||||||
|
|
||||||
# performance
|
# performance
|
||||||
|
|
||||||
defaults are good for most cases
|
defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload
|
||||||
|
|
||||||
you can ignore the `cannot efficiently use multiple CPU cores` message, it's very unlikely to be a problem
|
you can ignore the `cannot efficiently use multiple CPU cores` message, very unlikely to be a problem
|
||||||
|
|
||||||
below are some tweaks roughly ordered by usefulness:
|
below are some tweaks roughly ordered by usefulness:
|
||||||
|
|
||||||
@@ -730,6 +761,24 @@ below are some tweaks roughly ordered by usefulness:
|
|||||||
...however it adds an overhead to internal communication so it might be a net loss, see if it works 4 u
|
...however it adds an overhead to internal communication so it might be a net loss, see if it works 4 u
|
||||||
|
|
||||||
|
|
||||||
|
# security
|
||||||
|
|
||||||
|
some notes on hardening
|
||||||
|
|
||||||
|
on public copyparty instances with anonymous upload enabled:
|
||||||
|
|
||||||
|
* users can upload html/css/js which will evaluate for other visitors in a few ways,
|
||||||
|
* unless `--no-readme` is set: by uploading/modifying a file named `readme.md`
|
||||||
|
* if `move` access is granted AND none of `--no-logues`, `--no-dot-mv`, `--no-dot-ren` is set: by uploading some .html file and renaming it to `.epilogue.html` (uploading it directly is blocked)
|
||||||
|
|
||||||
|
|
||||||
|
## gotchas
|
||||||
|
|
||||||
|
behavior that might be unexpected
|
||||||
|
|
||||||
|
* users without read-access to a folder can still see the `.prologue.html` / `.epilogue.html` / `README.md` contents, for the purpose of showing a description on how to use the uploader for example
|
||||||
|
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
|
|
||||||
mandatory deps:
|
mandatory deps:
|
||||||
@@ -744,17 +793,11 @@ enable music tags:
|
|||||||
* either `mutagen` (fast, pure-python, skips a few tags, makes copyparty GPL? idk)
|
* either `mutagen` (fast, pure-python, skips a few tags, makes copyparty GPL? idk)
|
||||||
* or `ffprobe` (20x slower, more accurate, possibly dangerous depending on your distro and users)
|
* or `ffprobe` (20x slower, more accurate, possibly dangerous depending on your distro and users)
|
||||||
|
|
||||||
enable thumbnails of images:
|
enable [thumbnails](#thumbnails) of...
|
||||||
* `Pillow` (requires py2.7 or py3.5+)
|
* **images:** `Pillow` (requires py2.7 or py3.5+)
|
||||||
|
* **videos:** `ffmpeg` and `ffprobe` somewhere in `$PATH`
|
||||||
enable thumbnails of videos:
|
* **HEIF pictures:** `pyheif-pillow-opener` (requires Linux or a C compiler)
|
||||||
* `ffmpeg` and `ffprobe` somewhere in `$PATH`
|
* **AVIF pictures:** `pillow-avif-plugin`
|
||||||
|
|
||||||
enable thumbnails of HEIF pictures:
|
|
||||||
* `pyheif-pillow-opener` (requires Linux or a C compiler)
|
|
||||||
|
|
||||||
enable thumbnails of AVIF pictures:
|
|
||||||
* `pillow-avif-plugin`
|
|
||||||
|
|
||||||
|
|
||||||
## install recommended deps
|
## install recommended deps
|
||||||
@@ -791,8 +834,10 @@ if you don't need all the features, you can repack the sfx and save a bunch of s
|
|||||||
* `223k` after `./scripts/make-sfx.sh re no-ogv no-cm`
|
* `223k` after `./scripts/make-sfx.sh re no-ogv no-cm`
|
||||||
|
|
||||||
the features you can opt to drop are
|
the features you can opt to drop are
|
||||||
* `ogv`.js, the opus/vorbis decoder which is needed by apple devices to play foss audio files
|
* `ogv`.js, the opus/vorbis decoder which is needed by apple devices to play foss audio files, saves ~192k
|
||||||
* `cm`/easymde, the "fancy" markdown editor
|
* `cm`/easymde, the "fancy" markdown editor, saves ~92k
|
||||||
|
* `fnt`, source-code-pro, the monospace font, saves ~9k
|
||||||
|
* `dd`, the custom mouse cursor for the media player tray tab, saves ~2k
|
||||||
|
|
||||||
for the `re`pack to work, first run one of the sfx'es once to unpack it
|
for the `re`pack to work, first run one of the sfx'es once to unpack it
|
||||||
|
|
||||||
@@ -828,7 +873,7 @@ pip install black bandit pylint flake8 # vscode tooling
|
|||||||
|
|
||||||
## just the sfx
|
## just the sfx
|
||||||
|
|
||||||
grab the web-dependencies from a previous sfx (unless you need to modify something in those):
|
first grab the web-dependencies from a previous sfx (assuming you don't need to modify something in those):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
rm -rf copyparty/web/deps
|
rm -rf copyparty/web/deps
|
||||||
@@ -854,8 +899,8 @@ in the `scripts` folder:
|
|||||||
|
|
||||||
* run `make -C deps-docker` to build all dependencies
|
* run `make -C deps-docker` to build all dependencies
|
||||||
* `git tag v1.2.3 && git push origin --tags`
|
* `git tag v1.2.3 && git push origin --tags`
|
||||||
* create github release with `make-tgz-release.sh`
|
|
||||||
* upload to pypi with `make-pypi-release.(sh|bat)`
|
* upload to pypi with `make-pypi-release.(sh|bat)`
|
||||||
|
* create github release with `make-tgz-release.sh`
|
||||||
* create sfx with `make-sfx.sh`
|
* create sfx with `make-sfx.sh`
|
||||||
|
|
||||||
|
|
||||||
@@ -864,7 +909,6 @@ in the `scripts` folder:
|
|||||||
roughly sorted by priority
|
roughly sorted by priority
|
||||||
|
|
||||||
* hls framework for Someone Else to drop code into :^)
|
* hls framework for Someone Else to drop code into :^)
|
||||||
* readme.md as epilogue
|
|
||||||
|
|
||||||
|
|
||||||
## discarded ideas
|
## discarded ideas
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ however if your copyparty is behind a reverse-proxy, you may want to use [`share
|
|||||||
|
|
||||||
# OS integration
|
# OS integration
|
||||||
init-scripts to start copyparty as a service
|
init-scripts to start copyparty as a service
|
||||||
* [`systemd/copyparty.service`](systemd/copyparty.service)
|
* [`systemd/copyparty.service`](systemd/copyparty.service) runs the sfx normally
|
||||||
|
* [`systemd/prisonparty.service`](systemd/prisonparty.service) runs the sfx in a chroot
|
||||||
* [`openrc/copyparty`](openrc/copyparty)
|
* [`openrc/copyparty`](openrc/copyparty)
|
||||||
|
|
||||||
# Reverse-proxy
|
# Reverse-proxy
|
||||||
|
|||||||
@@ -360,6 +360,10 @@ def run_argparse(argv, formatter):
|
|||||||
ap2 = ap.add_argument_group('safety options')
|
ap2 = ap.add_argument_group('safety options')
|
||||||
ap2.add_argument("--ls", metavar="U[,V[,F]]", type=u, help="scan all volumes; arguments USER,VOL,FLAGS; example [**,*,ln,p,r]")
|
ap2.add_argument("--ls", metavar="U[,V[,F]]", type=u, help="scan all volumes; arguments USER,VOL,FLAGS; example [**,*,ln,p,r]")
|
||||||
ap2.add_argument("--salt", type=u, default="hunter2", help="up2k file-hash salt")
|
ap2.add_argument("--salt", type=u, default="hunter2", help="up2k file-hash salt")
|
||||||
|
ap2.add_argument("--no-dot-mv", action="store_true", help="disallow moving dotfiles; makes it impossible to move folders containing dotfiles")
|
||||||
|
ap2.add_argument("--no-dot-ren", action="store_true", help="disallow renaming dotfiles; makes it impossible to make something a dotfile")
|
||||||
|
ap2.add_argument("--no-logues", action="store_true", help="disable rendering .prologue/.epilogue.html into directory listings")
|
||||||
|
ap2.add_argument("--no-readme", action="store_true", help="disable rendering readme.md into directory listings")
|
||||||
|
|
||||||
ap2 = ap.add_argument_group('logging options')
|
ap2 = ap.add_argument_group('logging options')
|
||||||
ap2.add_argument("-q", action="store_true", help="quiet")
|
ap2.add_argument("-q", action="store_true", help="quiet")
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
VERSION = (0, 13, 8)
|
VERSION = (0, 13, 14)
|
||||||
CODENAME = "future-proof"
|
CODENAME = "future-proof"
|
||||||
BUILD_DT = (2021, 8, 29)
|
BUILD_DT = (2021, 9, 6)
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import gzip
|
|||||||
import time
|
import time
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
|
import base64
|
||||||
import string
|
import string
|
||||||
import socket
|
import socket
|
||||||
import ctypes
|
import ctypes
|
||||||
@@ -192,7 +193,21 @@ class HttpCli(object):
|
|||||||
self.cookies = cookies
|
self.cookies = cookies
|
||||||
self.vpath = unquotep(vpath) # not query, so + means +
|
self.vpath = unquotep(vpath) # not query, so + means +
|
||||||
|
|
||||||
pwd = uparam.get("pw")
|
pwd = None
|
||||||
|
ba = self.headers.get("authorization")
|
||||||
|
if ba:
|
||||||
|
try:
|
||||||
|
ba = ba.split(" ")[1].encode("ascii")
|
||||||
|
ba = base64.b64decode(ba).decode("utf-8")
|
||||||
|
# try "pwd", "x:pwd", "pwd:x"
|
||||||
|
for ba in [ba] + ba.split(":", 1)[::-1]:
|
||||||
|
if self.asrv.iacct.get(ba):
|
||||||
|
pwd = ba
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
pwd = uparam.get("pw") or pwd
|
||||||
self.uname = self.asrv.iacct.get(pwd, "*")
|
self.uname = self.asrv.iacct.get(pwd, "*")
|
||||||
self.rvol = self.asrv.vfs.aread[self.uname]
|
self.rvol = self.asrv.vfs.aread[self.uname]
|
||||||
self.wvol = self.asrv.vfs.awrite[self.uname]
|
self.wvol = self.asrv.vfs.awrite[self.uname]
|
||||||
@@ -873,7 +888,7 @@ class HttpCli(object):
|
|||||||
self.parser.drop()
|
self.parser.drop()
|
||||||
|
|
||||||
ck, msg = self.get_pwd_cookie(pwd)
|
ck, msg = self.get_pwd_cookie(pwd)
|
||||||
html = self.j2("msg", h1=msg, h2='<a href="/">ack</a>', redir="/")
|
html = self.j2("msg", h1=msg, h2='<a href="/?h">ack</a>', redir="/?h")
|
||||||
self.reply(html.encode("utf-8"), headers={"Set-Cookie": ck})
|
self.reply(html.encode("utf-8"), headers={"Set-Cookie": ck})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -1853,12 +1868,22 @@ class HttpCli(object):
|
|||||||
tpl = "browser2"
|
tpl = "browser2"
|
||||||
|
|
||||||
logues = ["", ""]
|
logues = ["", ""]
|
||||||
|
if not self.args.no_logues:
|
||||||
for n, fn in enumerate([".prologue.html", ".epilogue.html"]):
|
for n, fn in enumerate([".prologue.html", ".epilogue.html"]):
|
||||||
fn = os.path.join(abspath, fn)
|
fn = os.path.join(abspath, fn)
|
||||||
if bos.path.exists(fn):
|
if bos.path.exists(fn):
|
||||||
with open(fsenc(fn), "rb") as f:
|
with open(fsenc(fn), "rb") as f:
|
||||||
logues[n] = f.read().decode("utf-8")
|
logues[n] = f.read().decode("utf-8")
|
||||||
|
|
||||||
|
readme = ""
|
||||||
|
if not self.args.no_readme and not logues[1]:
|
||||||
|
for fn in ["README.md", "readme.md"]:
|
||||||
|
fn = os.path.join(abspath, fn)
|
||||||
|
if bos.path.exists(fn):
|
||||||
|
with open(fsenc(fn), "rb") as f:
|
||||||
|
readme = f.read().decode("utf-8")
|
||||||
|
break
|
||||||
|
|
||||||
ls_ret = {
|
ls_ret = {
|
||||||
"dirs": [],
|
"dirs": [],
|
||||||
"files": [],
|
"files": [],
|
||||||
@@ -1867,6 +1892,7 @@ class HttpCli(object):
|
|||||||
"acct": self.uname,
|
"acct": self.uname,
|
||||||
"perms": perms,
|
"perms": perms,
|
||||||
"logues": logues,
|
"logues": logues,
|
||||||
|
"readme": readme,
|
||||||
}
|
}
|
||||||
j2a = {
|
j2a = {
|
||||||
"vdir": quotep(self.vpath),
|
"vdir": quotep(self.vpath),
|
||||||
@@ -1885,6 +1911,7 @@ class HttpCli(object):
|
|||||||
"have_b_u": (self.can_write and self.uparam.get("b") == "u"),
|
"have_b_u": (self.can_write and self.uparam.get("b") == "u"),
|
||||||
"url_suf": url_suf,
|
"url_suf": url_suf,
|
||||||
"logues": logues,
|
"logues": logues,
|
||||||
|
"readme": readme,
|
||||||
"title": html_escape(self.vpath, crlf=True),
|
"title": html_escape(self.vpath, crlf=True),
|
||||||
"srv_info": srv_info,
|
"srv_info": srv_info,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This is Victor Stinner's pure-Python implementation of PEP 383: the "surrogateescape" error
|
This is Victor Stinner's pure-Python implementation of PEP 383: the "surrogateescape" error
|
||||||
handler of Python 3.
|
handler of Python 3.
|
||||||
@@ -171,7 +173,7 @@ FS_ENCODING = sys.getfilesystemencoding()
|
|||||||
|
|
||||||
if WINDOWS and not PY3:
|
if WINDOWS and not PY3:
|
||||||
# py2 thinks win* is mbcs, probably a bug? anyways this works
|
# py2 thinks win* is mbcs, probably a bug? anyways this works
|
||||||
FS_ENCODING = 'utf-8'
|
FS_ENCODING = "utf-8"
|
||||||
|
|
||||||
|
|
||||||
# normalize the filesystem encoding name.
|
# normalize the filesystem encoding name.
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ class TcpSrv(object):
|
|||||||
eps = self.ips_linux()
|
eps = self.ips_linux()
|
||||||
|
|
||||||
if "0.0.0.0" not in listen_ips:
|
if "0.0.0.0" not in listen_ips:
|
||||||
eps = {k: v for k, v in eps if k in listen_ips}
|
eps = {k: v for k, v in eps.items() if k in listen_ips}
|
||||||
|
|
||||||
default_route = None
|
default_route = None
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
|||||||
@@ -270,13 +270,14 @@ class ThumbSrv(object):
|
|||||||
fmts += ["RGBA", "LA"]
|
fmts += ["RGBA", "LA"]
|
||||||
args["method"] = 6
|
args["method"] = 6
|
||||||
else:
|
else:
|
||||||
pass # default q = 75
|
# default q = 75
|
||||||
|
args["progressive"] = True
|
||||||
|
|
||||||
if im.mode not in fmts:
|
if im.mode not in fmts:
|
||||||
# print("conv {}".format(im.mode))
|
# print("conv {}".format(im.mode))
|
||||||
im = im.convert("RGB")
|
im = im.convert("RGB")
|
||||||
|
|
||||||
im.save(tpath, quality=40, method=6)
|
im.save(tpath, **args)
|
||||||
|
|
||||||
def conv_ffmpeg(self, abspath, tpath):
|
def conv_ffmpeg(self, abspath, tpath):
|
||||||
ret, _ = ffprobe(abspath)
|
ret, _ = ffprobe(abspath)
|
||||||
|
|||||||
@@ -1520,6 +1520,14 @@ class Up2k(object):
|
|||||||
dabs = dvn.canonical(drem)
|
dabs = dvn.canonical(drem)
|
||||||
drd, dfn = vsplit(drem)
|
drd, dfn = vsplit(drem)
|
||||||
|
|
||||||
|
n1 = svp.split('/')[-1]
|
||||||
|
n2 = dvp.split('/')[-1]
|
||||||
|
if n1.startswith('.') or n2.startswith('.'):
|
||||||
|
if self.args.no_dot_mv:
|
||||||
|
raise Pebkac(400, "moving dotfiles was disabled by server config")
|
||||||
|
elif self.args.no_dot_ren and n1 != n2:
|
||||||
|
raise Pebkac(400, "renaming dotfiles was disabled by server config")
|
||||||
|
|
||||||
if bos.path.exists(dabs):
|
if bos.path.exists(dabs):
|
||||||
raise Pebkac(400, "mv2: target file exists")
|
raise Pebkac(400, "mv2: target file exists")
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,6 @@
|
|||||||
--grid-sz: 10em;
|
--grid-sz: 10em;
|
||||||
--grid-ln: 3;
|
--grid-ln: 3;
|
||||||
}
|
}
|
||||||
@font-face {
|
|
||||||
font-family: 'scp';
|
|
||||||
src: local('Source Code Pro Regular'), local('SourceCodePro-Regular'), url(/.cpr/deps/scp.woff2) format('woff2');
|
|
||||||
}
|
|
||||||
* {
|
* {
|
||||||
line-height: 1.2em;
|
line-height: 1.2em;
|
||||||
}
|
}
|
||||||
@@ -177,6 +173,10 @@ a, #files tbody div a:last-child {
|
|||||||
#epi.logue {
|
#epi.logue {
|
||||||
margin: .8em 0;
|
margin: .8em 0;
|
||||||
}
|
}
|
||||||
|
.mdo,
|
||||||
|
.mdo * {
|
||||||
|
line-height: 1.4em;
|
||||||
|
}
|
||||||
#srv_info {
|
#srv_info {
|
||||||
color: #a73;
|
color: #a73;
|
||||||
background: #333;
|
background: #333;
|
||||||
@@ -204,6 +204,9 @@ a, #files tbody div a:last-child {
|
|||||||
color: #f4c;
|
color: #f4c;
|
||||||
border-bottom: 1px solid rgba(255,68,204,0.6);
|
border-bottom: 1px solid rgba(255,68,204,0.6);
|
||||||
}
|
}
|
||||||
|
#repl {
|
||||||
|
padding: .33em;
|
||||||
|
}
|
||||||
#files tbody a.play {
|
#files tbody a.play {
|
||||||
color: #e70;
|
color: #e70;
|
||||||
padding: .2em;
|
padding: .2em;
|
||||||
@@ -340,17 +343,19 @@ html.light #ggrid>a.sel {
|
|||||||
}
|
}
|
||||||
#wtgrid,
|
#wtgrid,
|
||||||
#wtico {
|
#wtico {
|
||||||
cursor: url(/.cpr/dd/4.png), pointer;
|
|
||||||
animation: cursor 500ms;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: .9em;
|
font-size: .9em;
|
||||||
top: -.04em;
|
top: -.04em;
|
||||||
}
|
}
|
||||||
#wtgrid {
|
#wtgrid {
|
||||||
font-size: .75em;
|
font-size: .75em;
|
||||||
|
padding: .1em;
|
||||||
top: -.12em;
|
top: -.12em;
|
||||||
}
|
}
|
||||||
#wtgrid:hover,
|
#wtico {
|
||||||
|
cursor: url(/.cpr/dd/4.png), pointer;
|
||||||
|
animation: cursor 500ms;
|
||||||
|
}
|
||||||
#wtico:hover {
|
#wtico:hover {
|
||||||
animation: cursor 500ms infinite;
|
animation: cursor 500ms infinite;
|
||||||
}
|
}
|
||||||
@@ -378,23 +383,25 @@ html.light #ggrid>a.sel {
|
|||||||
background: #3c3c3c;
|
background: #3c3c3c;
|
||||||
box-shadow: 0 0 .5em #222;
|
box-shadow: 0 0 .5em #222;
|
||||||
border-radius: .3em 0 0 0;
|
border-radius: .3em 0 0 0;
|
||||||
padding: 0;
|
padding: 0 0 0 .1em;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
#wzip, #wnp {
|
#wfm, #wzip, #wnp {
|
||||||
display: none;
|
display: none;
|
||||||
margin-right: .3em;
|
}
|
||||||
padding-right: .3em;
|
#wzip, #wnp {
|
||||||
|
margin-right: .2em;
|
||||||
|
padding-right: .2em;
|
||||||
border-right: .1em solid #555;
|
border-right: .1em solid #555;
|
||||||
}
|
}
|
||||||
#wnp a {
|
#wfm.act+#wzip,
|
||||||
position: relative;
|
#wfm.act+#wzip+#wnp {
|
||||||
font-size: .47em;
|
margin-left: .2em;
|
||||||
margin: 0 .1em;
|
padding-left: .2em;
|
||||||
top: -.4em;
|
border-left: .1em solid #555;
|
||||||
}
|
}
|
||||||
#wnp a+a {
|
#wfm.act {
|
||||||
margin-left: .33em;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
#wtoggle,
|
#wtoggle,
|
||||||
#wtoggle * {
|
#wtoggle * {
|
||||||
@@ -408,6 +415,7 @@ html.light #ggrid>a.sel {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#wfm a,
|
#wfm a,
|
||||||
|
#wnp a,
|
||||||
#wzip a {
|
#wzip a {
|
||||||
font-size: .5em;
|
font-size: .5em;
|
||||||
padding: 0 .3em;
|
padding: 0 .3em;
|
||||||
@@ -415,10 +423,14 @@ html.light #ggrid>a.sel {
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
#wfm span {
|
#wfm span,
|
||||||
|
#wnp span {
|
||||||
font-size: .6em;
|
font-size: .6em;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
#wnp span {
|
||||||
|
font-size: .7em;
|
||||||
|
}
|
||||||
#wfm a:not(.en) {
|
#wfm a:not(.en) {
|
||||||
opacity: .3;
|
opacity: .3;
|
||||||
color: #f6c;
|
color: #f6c;
|
||||||
@@ -803,8 +815,6 @@ input.eq_gain {
|
|||||||
width: 1em;
|
width: 1em;
|
||||||
border-radius: .2em;
|
border-radius: .2em;
|
||||||
margin: -1.2em auto 0 auto;
|
margin: -1.2em auto 0 auto;
|
||||||
top: 2em;
|
|
||||||
position: relative;
|
|
||||||
background: #444;
|
background: #444;
|
||||||
}
|
}
|
||||||
#files th span {
|
#files th span {
|
||||||
@@ -1532,6 +1542,8 @@ html.light #bbox-overlay figcaption a {
|
|||||||
|
|
||||||
#op_up2k {
|
#op_up2k {
|
||||||
padding: 0 1em 1em 1em;
|
padding: 0 1em 1em 1em;
|
||||||
|
min-height: 0;
|
||||||
|
transition: min-height .2s;
|
||||||
}
|
}
|
||||||
#u2form {
|
#u2form {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -114,6 +114,8 @@
|
|||||||
|
|
||||||
<h2><a href="/?h">control-panel</a></h2>
|
<h2><a href="/?h">control-panel</a></h2>
|
||||||
|
|
||||||
|
<a href="#" id="repl">π</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{%- if srv_info %}
|
{%- if srv_info %}
|
||||||
@@ -131,7 +133,8 @@
|
|||||||
have_mv = {{ have_mv|tojson }},
|
have_mv = {{ have_mv|tojson }},
|
||||||
have_del = {{ have_del|tojson }},
|
have_del = {{ have_del|tojson }},
|
||||||
have_unpost = {{ have_unpost|tojson }},
|
have_unpost = {{ have_unpost|tojson }},
|
||||||
have_zip = {{ have_zip|tojson }};
|
have_zip = {{ have_zip|tojson }},
|
||||||
|
readme = {{ readme|tojson }};
|
||||||
</script>
|
</script>
|
||||||
<script src="/.cpr/util.js?_={{ ts }}"></script>
|
<script src="/.cpr/util.js?_={{ ts }}"></script>
|
||||||
<script src="/.cpr/browser.js?_={{ ts }}"></script>
|
<script src="/.cpr/browser.js?_={{ ts }}"></script>
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ ebi('widget').innerHTML = (
|
|||||||
' href="#" id="selinv" tt="invert selection">sel.<br />inv.</a><a' +
|
' href="#" id="selinv" tt="invert selection">sel.<br />inv.</a><a' +
|
||||||
' href="#" id="selzip" tt="download selection as archive">zip</a>' +
|
' href="#" id="selzip" tt="download selection as archive">zip</a>' +
|
||||||
'</span><span id="wnp"><a' +
|
'</span><span id="wnp"><a' +
|
||||||
' href="#" id="npirc" tt="copy irc-formatted track info">📋irc</a><a' +
|
' href="#" id="npirc" tt="copy irc-formatted track info">📋<span>irc</span></a><a' +
|
||||||
' href="#" id="nptxt" tt="copy plaintext track info">📋txt</a>' +
|
' href="#" id="nptxt" tt="copy plaintext track info">📋<span>txt</span></a>' +
|
||||||
'</span><a' +
|
'</span><a' +
|
||||||
' href="#" id="wtgrid" tt="toggle grid/list view$NHotkey: G">田</a><a' +
|
' href="#" id="wtgrid" tt="toggle grid/list view$NHotkey: G">田</a><a' +
|
||||||
' href="#" id="wtico">♫</a>' +
|
' href="#" id="wtico">♫</a>' +
|
||||||
@@ -140,9 +140,10 @@ ebi('op_cfg').innerHTML = (
|
|||||||
' <div>\n' +
|
' <div>\n' +
|
||||||
' <a id="tooltips" class="tgl btn" href="#" tt="◔ ◡ ◔">ℹ️ tooltips</a>\n' +
|
' <a id="tooltips" class="tgl btn" href="#" tt="◔ ◡ ◔">ℹ️ tooltips</a>\n' +
|
||||||
' <a id="lightmode" class="tgl btn" href="#">☀️ lightmode</a>\n' +
|
' <a id="lightmode" class="tgl btn" href="#">☀️ lightmode</a>\n' +
|
||||||
' <a id="dotfiles" class="tgl btn" href="#" tt="show hidden files (if server permits)">dotfiles</a>\n' +
|
|
||||||
' <a id="griden" class="tgl btn" href="#" tt="toggle icons or list-view$NHotkey: G">田 the grid</a>\n' +
|
' <a id="griden" class="tgl btn" href="#" tt="toggle icons or list-view$NHotkey: G">田 the grid</a>\n' +
|
||||||
' <a id="thumbs" class="tgl btn" href="#" tt="in icon view, toggle icons or thumbnails$NHotkey: T">🖼️ thumbs</a>\n' +
|
' <a id="thumbs" class="tgl btn" href="#" tt="in icon view, toggle icons or thumbnails$NHotkey: T">🖼️ thumbs</a>\n' +
|
||||||
|
' <a id="dotfiles" class="tgl btn" href="#" tt="show hidden files (if server permits)">dotfiles</a>\n' +
|
||||||
|
' <a id="ireadme" class="tgl btn" href="#" tt="show README.md in folder listings">📜 readme</a>\n' +
|
||||||
' </div>\n' +
|
' </div>\n' +
|
||||||
'</div>\n' +
|
'</div>\n' +
|
||||||
(have_zip ? (
|
(have_zip ? (
|
||||||
@@ -284,7 +285,7 @@ var mpl = (function () {
|
|||||||
r.os_ctl = !r.os_ctl && have_mctl;
|
r.os_ctl = !r.os_ctl && have_mctl;
|
||||||
bcfg_set('au_os_ctl', r.os_ctl);
|
bcfg_set('au_os_ctl', r.os_ctl);
|
||||||
if (!have_mctl)
|
if (!have_mctl)
|
||||||
toast.err(5, 'need firefox 82+ or chrome 73+');
|
toast.err(5, 'need firefox 82+ or chrome 73+\n(or iOS 15+ supposedly)');
|
||||||
};
|
};
|
||||||
|
|
||||||
ebi('au_osd_cv').onclick = function (e) {
|
ebi('au_osd_cv').onclick = function (e) {
|
||||||
@@ -704,7 +705,7 @@ var pbar = (function () {
|
|||||||
|
|
||||||
pctx.clearRect(0, 0, pc.w, pc.h);
|
pctx.clearRect(0, 0, pc.w, pc.h);
|
||||||
|
|
||||||
if (!mp.au || mp.loading || isNaN(adur = mp.au.duration) || isNaN(apos = mp.au.currentTime))
|
if (!mp.au || mp.loading || isNaN(adur = mp.au.duration) || isNaN(apos = mp.au.currentTime) || apos < 0 || adur < apos)
|
||||||
return; // not-init || unsupp-codec
|
return; // not-init || unsupp-codec
|
||||||
|
|
||||||
var sm = bc.w * 1.0 / adur;
|
var sm = bc.w * 1.0 / adur;
|
||||||
@@ -1252,6 +1253,9 @@ function play(tid, is_ev, seek, call_depth) {
|
|||||||
if (mp.order.length == 0)
|
if (mp.order.length == 0)
|
||||||
return console.log('no audio found wait what');
|
return console.log('no audio found wait what');
|
||||||
|
|
||||||
|
if (crashed)
|
||||||
|
return;
|
||||||
|
|
||||||
mp.stopfade(true);
|
mp.stopfade(true);
|
||||||
|
|
||||||
var tn = tid;
|
var tn = tid;
|
||||||
@@ -1293,12 +1297,21 @@ function play(tid, is_ev, seek, call_depth) {
|
|||||||
|
|
||||||
var url = mp.tracks[tid];
|
var url = mp.tracks[tid];
|
||||||
if (need_ogv_for(url)) {
|
if (need_ogv_for(url)) {
|
||||||
|
var m = /.* Version\/([0-9]+)\.[0-9\.]+ Mobile\/[^ ]+ Safari\/[0-9\.]+$/.exec(navigator.userAgent),
|
||||||
|
safari = m ? parseInt(m[1]) : 99;
|
||||||
|
|
||||||
if (mp.au_ogvjs) {
|
if (mp.au_ogvjs) {
|
||||||
mp.au = mp.au_ogvjs;
|
mp.au = mp.au_ogvjs;
|
||||||
}
|
}
|
||||||
else if (window['OGVPlayer']) {
|
else if (window['OGVPlayer']) {
|
||||||
mp.loading = true;
|
mp.loading = true;
|
||||||
|
try {
|
||||||
mp.au = mp.au_ogvjs = new OGVPlayer();
|
mp.au = mp.au_ogvjs = new OGVPlayer();
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
return toast.err(30, 'your browser cannot play ogg/vorbis/opus\n\n' + ex +
|
||||||
|
'\n\n<a href="#" onclick="new OGVPlayer();">click here</a> for a full crash report');
|
||||||
|
}
|
||||||
attempt_play = is_ev;
|
attempt_play = is_ev;
|
||||||
mp.au.onerror = evau_error;
|
mp.au.onerror = evau_error;
|
||||||
mp.au.onprogress = pbar.drawpos;
|
mp.au.onprogress = pbar.drawpos;
|
||||||
@@ -1308,6 +1321,9 @@ function play(tid, is_ev, seek, call_depth) {
|
|||||||
};
|
};
|
||||||
widget.open();
|
widget.open();
|
||||||
}
|
}
|
||||||
|
else if (safari < 14) {
|
||||||
|
return toast.err(0, 'because this is an apple device,\nsafari 14 or newer is required to play ogg/vorbis/opus files\n\nyou are using safari ' + safari + '\n(every iOS browser is actually safari)');
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
if (call_depth !== undefined)
|
if (call_depth !== undefined)
|
||||||
return toast.err(0, 'failed to load ogv.js:\ncannot play ogg/opus in this browser\n(try a non-apple device)');
|
return toast.err(0, 'failed to load ogv.js:\ncannot play ogg/opus in this browser\n(try a non-apple device)');
|
||||||
@@ -1317,10 +1333,6 @@ function play(tid, is_ev, seek, call_depth) {
|
|||||||
import_js('/.cpr/deps/ogv.js', function () {
|
import_js('/.cpr/deps/ogv.js', function () {
|
||||||
toast.hide();
|
toast.hide();
|
||||||
play(tid, false, seek, 1);
|
play(tid, false, seek, 1);
|
||||||
|
|
||||||
var m = /.* Version\/([0-9]+)\.[0-9\.]+ Mobile\/[^ ]+ Safari\/[0-9\.]+$/.exec(navigator.userAgent);
|
|
||||||
if (m && parseInt(m[1]) < 14)
|
|
||||||
toast.err(0, 'because this is an apple device,\nsafari 14 or newer is required\n\nyou are using safari version ' + m[1] + ', so playback of ogg/vorbis/opus files will be buggy\n\nnote: every iOS browser is safari');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -1710,7 +1722,7 @@ var fileman = (function () {
|
|||||||
bcut.style.display = have_mv && has(perms, 'move') ? '' : 'none';
|
bcut.style.display = have_mv && has(perms, 'move') ? '' : 'none';
|
||||||
bpst.style.display = have_mv && has(perms, 'write') ? '' : 'none';
|
bpst.style.display = have_mv && has(perms, 'write') ? '' : 'none';
|
||||||
bpst.setAttribute('tt', 'paste ' + r.clip.length + ' items$NHotkey: ctrl-V');
|
bpst.setAttribute('tt', 'paste ' + r.clip.length + ' items$NHotkey: ctrl-V');
|
||||||
ebi('wfm').style.display = QS('#wfm a.en:not([display])') ? '' : 'none';
|
clmod(ebi('wfm'), 'act', QS('#wfm a.en:not([style])'));
|
||||||
};
|
};
|
||||||
|
|
||||||
r.rename = function (e) {
|
r.rename = function (e) {
|
||||||
@@ -1777,7 +1789,7 @@ var fileman = (function () {
|
|||||||
|
|
||||||
html = html.concat([
|
html = html.concat([
|
||||||
'<button id="rn_cancel" tt="abort and close this window">❌ cancel</button>',
|
'<button id="rn_cancel" tt="abort and close this window">❌ cancel</button>',
|
||||||
'<button id="rn_apply">✅ apply rename</button>',
|
'<button id="rn_apply">✅ APPLY RENAME</button>',
|
||||||
'<a id="rn_adv" class="tgl btn" href="#" tt="batch / metadata / pattern renaming">advanced</a>',
|
'<a id="rn_adv" class="tgl btn" href="#" tt="batch / metadata / pattern renaming">advanced</a>',
|
||||||
'<a id="rn_case" class="tgl btn" href="#" tt="case-sensitive regex">case</a>',
|
'<a id="rn_case" class="tgl btn" href="#" tt="case-sensitive regex">case</a>',
|
||||||
'</div>',
|
'</div>',
|
||||||
@@ -1791,6 +1803,7 @@ var fileman = (function () {
|
|||||||
'</table></div>'
|
'</table></div>'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
var cheap = f.length > 500;
|
||||||
if (sel.length == 1)
|
if (sel.length == 1)
|
||||||
html.push(
|
html.push(
|
||||||
'<div><table id="rn_f">\n' +
|
'<div><table id="rn_f">\n' +
|
||||||
@@ -1803,8 +1816,9 @@ var fileman = (function () {
|
|||||||
for (var a = 0; a < f.length; a++)
|
for (var a = 0; a < f.length; a++)
|
||||||
html.push(
|
html.push(
|
||||||
'<tr><td>' +
|
'<tr><td>' +
|
||||||
'<button class="rn_dec" n="' + a + '">decode</button>',
|
(cheap ? '</td>' :
|
||||||
'<button class="rn_reset" n="' + a + '">↺ reset</button></td>',
|
'<button class="rn_dec" n="' + a + '">decode</button>' +
|
||||||
|
'<button class="rn_reset" n="' + a + '">↺ reset</button></td>') +
|
||||||
'<td><input type="text" id="rn_new" n="' + a + '" /></td>' +
|
'<td><input type="text" id="rn_new" n="' + a + '" /></td>' +
|
||||||
'<td><input type="text" id="rn_old" n="' + a + '" readonly /></td></tr>');
|
'<td><input type="text" id="rn_old" n="' + a + '" readonly /></td></tr>');
|
||||||
}
|
}
|
||||||
@@ -1825,6 +1839,7 @@ var fileman = (function () {
|
|||||||
f[a].inew = QS('#rn_new' + k);
|
f[a].inew = QS('#rn_new' + k);
|
||||||
f[a].inew.value = f[a].iold.value = f[a].ofn;
|
f[a].inew.value = f[a].iold.value = f[a].ofn;
|
||||||
|
|
||||||
|
if (!cheap)
|
||||||
(function (a) {
|
(function (a) {
|
||||||
f[a].inew.onkeydown = function (e) {
|
f[a].inew.onkeydown = function (e) {
|
||||||
rn_ok(a, true);
|
rn_ok(a, true);
|
||||||
@@ -1835,10 +1850,12 @@ var fileman = (function () {
|
|||||||
if (e.key == 'Enter')
|
if (e.key == 'Enter')
|
||||||
return rn_apply();
|
return rn_apply();
|
||||||
};
|
};
|
||||||
QS('.rn_dec' + k).onclick = function () {
|
QS('.rn_dec' + k).onclick = function (e) {
|
||||||
|
ev(e);
|
||||||
f[a].inew.value = uricom_dec(f[a].inew.value)[0];
|
f[a].inew.value = uricom_dec(f[a].inew.value)[0];
|
||||||
};
|
};
|
||||||
QS('.rn_reset' + k).onclick = function () {
|
QS('.rn_reset' + k).onclick = function (e) {
|
||||||
|
ev(e);
|
||||||
rn_reset(a);
|
rn_reset(a);
|
||||||
};
|
};
|
||||||
})(a);
|
})(a);
|
||||||
@@ -1864,18 +1881,21 @@ var fileman = (function () {
|
|||||||
f[n].inew.focus();
|
f[n].inew.focus();
|
||||||
f[n].inew.setSelectionRange(0, f[n].inew.value.lastIndexOf('.'), "forward");
|
f[n].inew.setSelectionRange(0, f[n].inew.value.lastIndexOf('.'), "forward");
|
||||||
}
|
}
|
||||||
function rn_cancel() {
|
function rn_cancel(e) {
|
||||||
|
ev(e);
|
||||||
rui.parentNode.removeChild(rui);
|
rui.parentNode.removeChild(rui);
|
||||||
}
|
}
|
||||||
|
|
||||||
ebi('rn_cancel').onclick = rn_cancel;
|
ebi('rn_cancel').onclick = rn_cancel;
|
||||||
ebi('rn_apply').onclick = rn_apply;
|
ebi('rn_apply').onclick = rn_apply;
|
||||||
ebi('rn_adv').onclick = function () {
|
ebi('rn_adv').onclick = function (e) {
|
||||||
|
ev(e);
|
||||||
adv = !adv;
|
adv = !adv;
|
||||||
bcfg_set('rn_adv', adv);
|
bcfg_set('rn_adv', adv);
|
||||||
sadv();
|
sadv();
|
||||||
};
|
};
|
||||||
ebi('rn_case').onclick = function () {
|
ebi('rn_case').onclick = function (e) {
|
||||||
|
ev(e);
|
||||||
cs = !cs;
|
cs = !cs;
|
||||||
bcfg_set('rn_case', cs);
|
bcfg_set('rn_case', cs);
|
||||||
};
|
};
|
||||||
@@ -1902,7 +1922,8 @@ var fileman = (function () {
|
|||||||
ipre.appendChild(o);
|
ipre.appendChild(o);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inew.onclick = function () {
|
inew.onclick = function (e) {
|
||||||
|
ev(e);
|
||||||
modal.prompt('provide a name for your new preset', ifmt.value, function (name) {
|
modal.prompt('provide a name for your new preset', ifmt.value, function (name) {
|
||||||
if (!name)
|
if (!name)
|
||||||
return toast.warn(3, 'aborted');
|
return toast.warn(3, 'aborted');
|
||||||
@@ -1913,7 +1934,8 @@ var fileman = (function () {
|
|||||||
ipre.value = name;
|
ipre.value = name;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
idel.onclick = function () {
|
idel.onclick = function (e) {
|
||||||
|
ev(e);
|
||||||
delete presets[ipre.value];
|
delete presets[ipre.value];
|
||||||
jwrite('rn_pre', presets);
|
jwrite('rn_pre', presets);
|
||||||
spresets();
|
spresets();
|
||||||
@@ -1971,7 +1993,8 @@ var fileman = (function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function rn_apply() {
|
function rn_apply(e) {
|
||||||
|
ev(e);
|
||||||
while (f.length && (!f[0].ok || f[0].ofn == f[0].inew.value))
|
while (f.length && (!f[0].ok || f[0].ofn == f[0].inew.value))
|
||||||
f.shift();
|
f.shift();
|
||||||
|
|
||||||
@@ -2076,9 +2099,18 @@ var fileman = (function () {
|
|||||||
clmod(els[a], 'fcut', 1);
|
clmod(els[a], 'fcut', 1);
|
||||||
}, 1);
|
}, 1);
|
||||||
|
|
||||||
toast.inf(1.5, 'cut ' + sel.length + ' items');
|
try {
|
||||||
jwrite('fman_clip', vps);
|
vps = JSON.stringify(vps);
|
||||||
|
if (vps.length > 1024 * 1024)
|
||||||
|
throw 'a';
|
||||||
|
|
||||||
|
swrite('fman_clip', vps);
|
||||||
r.tx(1);
|
r.tx(1);
|
||||||
|
toast.inf(1.5, 'cut ' + sel.length + ' items');
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
toast.warn(30, 'cut ' + sel.length + ' items\n\nbut: only <b>this</b> browser-tab can paste them\n(since the selection is so absolutely massive)');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
r.paste = function (e) {
|
r.paste = function (e) {
|
||||||
@@ -2365,7 +2397,8 @@ var thegrid = (function () {
|
|||||||
}
|
}
|
||||||
var uns = QS('#ggrid a[ref="unsearch"]');
|
var uns = QS('#ggrid a[ref="unsearch"]');
|
||||||
if (uns)
|
if (uns)
|
||||||
uns.onclick = function () {
|
uns.onclick = function (e) {
|
||||||
|
ev(e);
|
||||||
ebi('unsearch').click();
|
ebi('unsearch').click();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -2687,10 +2720,10 @@ document.onkeydown = function (e) {
|
|||||||
return ebi('gridsel').click();
|
return ebi('gridsel').click();
|
||||||
|
|
||||||
if (k == 'KeyA')
|
if (k == 'KeyA')
|
||||||
return QSA('#ghead>a[z]')[0].click();
|
return QSA('#ghead a[z]')[0].click();
|
||||||
|
|
||||||
if (k == 'KeyD')
|
if (k == 'KeyD')
|
||||||
return QSA('#ghead>a[z]')[1].click();
|
return QSA('#ghead a[z]')[1].click();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2967,7 +3000,8 @@ var treectl = (function () {
|
|||||||
var treectl = {
|
var treectl = {
|
||||||
"hidden": true,
|
"hidden": true,
|
||||||
"ls_cb": null,
|
"ls_cb": null,
|
||||||
"dir_cb": tree_scrollto
|
"dir_cb": tree_scrollto,
|
||||||
|
"ireadme": bcfg_get('ireadme', true)
|
||||||
},
|
},
|
||||||
entreed = false,
|
entreed = false,
|
||||||
fixedpos = false,
|
fixedpos = false,
|
||||||
@@ -3288,6 +3322,12 @@ var treectl = (function () {
|
|||||||
ebi('pro').innerHTML = res.logues ? res.logues[0] || "" : "";
|
ebi('pro').innerHTML = res.logues ? res.logues[0] || "" : "";
|
||||||
ebi('epi').innerHTML = res.logues ? res.logues[1] || "" : "";
|
ebi('epi').innerHTML = res.logues ? res.logues[1] || "" : "";
|
||||||
|
|
||||||
|
clmod(ebi('epi'), 'mdo');
|
||||||
|
if (res.readme)
|
||||||
|
setTimeout(function () {
|
||||||
|
show_readme(res.readme);
|
||||||
|
}, 10);
|
||||||
|
|
||||||
document.title = '⇆🎉 ' + uricom_dec(document.location.pathname.slice(1, -1))[0];
|
document.title = '⇆🎉 ' + uricom_dec(document.location.pathname.slice(1, -1))[0];
|
||||||
|
|
||||||
filecols.set_style();
|
filecols.set_style();
|
||||||
@@ -3340,6 +3380,12 @@ var treectl = (function () {
|
|||||||
treectl.goto(get_evpath());
|
treectl.goto(get_evpath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function treadme(e) {
|
||||||
|
ev(e);
|
||||||
|
treectl.ireadme = !treectl.ireadme;
|
||||||
|
bcfg_set('ireadme', treectl.ireadme);
|
||||||
|
}
|
||||||
|
|
||||||
function dyntree(e) {
|
function dyntree(e) {
|
||||||
ev(e);
|
ev(e);
|
||||||
dyn = !dyn;
|
dyn = !dyn;
|
||||||
@@ -3361,6 +3407,7 @@ var treectl = (function () {
|
|||||||
ebi('detree').onclick = treectl.detree;
|
ebi('detree').onclick = treectl.detree;
|
||||||
ebi('visdir').onclick = tree_scrollto;
|
ebi('visdir').onclick = tree_scrollto;
|
||||||
ebi('dotfiles').onclick = tdots;
|
ebi('dotfiles').onclick = tdots;
|
||||||
|
ebi('ireadme').onclick = treadme;
|
||||||
ebi('dyntree').onclick = dyntree;
|
ebi('dyntree').onclick = dyntree;
|
||||||
ebi('twig').onclick = scaletree;
|
ebi('twig').onclick = scaletree;
|
||||||
ebi('twobytwo').onclick = scaletree;
|
ebi('twobytwo').onclick = scaletree;
|
||||||
@@ -3562,6 +3609,7 @@ var filecols = (function () {
|
|||||||
if (ttv) {
|
if (ttv) {
|
||||||
th.setAttribute("tt", ttv);
|
th.setAttribute("tt", ttv);
|
||||||
th.setAttribute("ttd", "u");
|
th.setAttribute("ttd", "u");
|
||||||
|
th.setAttribute("ttm", "12");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -3790,6 +3838,7 @@ var light;
|
|||||||
|
|
||||||
function freshen() {
|
function freshen() {
|
||||||
clmod(document.documentElement, "light", light);
|
clmod(document.documentElement, "light", light);
|
||||||
|
clmod(document.documentElement, "dark", !light);
|
||||||
pbar.drawbuf();
|
pbar.drawbuf();
|
||||||
pbar.drawpos();
|
pbar.drawpos();
|
||||||
vbar.draw();
|
vbar.draw();
|
||||||
@@ -4003,6 +4052,51 @@ var msel = (function () {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
function show_readme(md, url, depth) {
|
||||||
|
if (!treectl.ireadme)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var div = ebi('epi'),
|
||||||
|
errmsg = 'cannot show README.md:\n\n',
|
||||||
|
now = window.location.href.replace(/\/?[?#].*/, "");
|
||||||
|
|
||||||
|
url = url || now;
|
||||||
|
if (url != now)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!window['marked']) {
|
||||||
|
if (depth)
|
||||||
|
return toast.warn(10, errmsg + 'failed to load marked.js')
|
||||||
|
|
||||||
|
return import_js('/.cpr/deps/marked.js', function () {
|
||||||
|
show_readme(md, url, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
clmod(div, 'mdo', 1);
|
||||||
|
div.innerHTML = marked(md, {
|
||||||
|
headerPrefix: 'md-',
|
||||||
|
breaks: true,
|
||||||
|
gfm: true
|
||||||
|
});
|
||||||
|
var links = QSA('#epi a');
|
||||||
|
for (var a = 0, aa = links.length; a < aa; a++) {
|
||||||
|
var href = links[a].getAttribute('href');
|
||||||
|
if (!href.startsWith('#'))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
links[a].setAttribute('href', '#md-' + href.slice(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
toast.warn(10, errmsg + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (readme)
|
||||||
|
show_readme(readme);
|
||||||
|
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
try {
|
try {
|
||||||
var tr = ebi('files').tBodies[0].rows;
|
var tr = ebi('files').tBodies[0].rows;
|
||||||
@@ -4125,6 +4219,7 @@ var unpost = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ct.onclick = function (e) {
|
ct.onclick = function (e) {
|
||||||
|
ev(e);
|
||||||
var tgt = e.target.closest('a[me]');
|
var tgt = e.target.closest('a[me]');
|
||||||
if (!tgt)
|
if (!tgt)
|
||||||
return;
|
return;
|
||||||
@@ -4166,7 +4261,8 @@ var unpost = (function () {
|
|||||||
tfilt = setTimeout(r.load, 250);
|
tfilt = setTimeout(r.load, 250);
|
||||||
};
|
};
|
||||||
|
|
||||||
ebi('unpost_nofilt').onclick = function () {
|
ebi('unpost_nofilt').onclick = function (e) {
|
||||||
|
ev(e);
|
||||||
filt.value = '';
|
filt.value = '';
|
||||||
r.load();
|
r.load();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
@font-face {
|
|
||||||
font-family: 'scp';
|
|
||||||
src: local('Source Code Pro Regular'), local('SourceCodePro-Regular'), url(/.cpr/deps/scp.woff2) format('woff2');
|
|
||||||
}
|
|
||||||
html, body {
|
html, body {
|
||||||
color: #333;
|
color: #333;
|
||||||
background: #eee;
|
background: #eee;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
}
|
}
|
||||||
|
#repl {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: .5em;
|
||||||
|
border: none;
|
||||||
|
color: inherit;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
#mtw {
|
#mtw {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -19,122 +23,8 @@ html, body {
|
|||||||
bottom: auto;
|
bottom: auto;
|
||||||
top: 1.4em;
|
top: 1.4em;
|
||||||
}
|
}
|
||||||
pre, code, a {
|
a {
|
||||||
color: #480;
|
text-decoration: none;
|
||||||
background: #f7f7f7;
|
|
||||||
border: .07em solid #ddd;
|
|
||||||
border-radius: .2em;
|
|
||||||
padding: .1em .3em;
|
|
||||||
margin: 0 .1em;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
font-size: .96em;
|
|
||||||
}
|
|
||||||
pre, code, tt {
|
|
||||||
font-family: 'scp', monospace, monospace;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
counter-reset: precode;
|
|
||||||
}
|
|
||||||
pre code {
|
|
||||||
counter-increment: precode;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 -.3em;
|
|
||||||
padding: .4em .5em;
|
|
||||||
border: none;
|
|
||||||
border-bottom: 1px solid #cdc;
|
|
||||||
min-width: calc(100% - .6em);
|
|
||||||
line-height: 1.1em;
|
|
||||||
}
|
|
||||||
pre code:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
pre code::before {
|
|
||||||
content: counter(precode);
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
display: inline-block;
|
|
||||||
text-align: right;
|
|
||||||
font-size: .75em;
|
|
||||||
color: #48a;
|
|
||||||
width: 4em;
|
|
||||||
padding-right: 1.5em;
|
|
||||||
margin-left: -5.5em;
|
|
||||||
}
|
|
||||||
pre code:hover {
|
|
||||||
background: #fec;
|
|
||||||
color: #360;
|
|
||||||
}
|
|
||||||
h1, h2 {
|
|
||||||
line-height: 1.5em;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 1.7em;
|
|
||||||
text-align: center;
|
|
||||||
border: 1em solid #777;
|
|
||||||
border-width: .05em 0;
|
|
||||||
margin: 3em 0;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: 1.5em;
|
|
||||||
font-weight: normal;
|
|
||||||
background: #f7f7f7;
|
|
||||||
border-top: .07em solid #fff;
|
|
||||||
border-bottom: .07em solid #bbb;
|
|
||||||
border-radius: .5em .5em 0 0;
|
|
||||||
padding-left: .4em;
|
|
||||||
margin-top: 3em;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
border-bottom: .1em solid #999;
|
|
||||||
}
|
|
||||||
h1 a, h3 a, h5 a,
|
|
||||||
h2 a, h4 a, h6 a {
|
|
||||||
color: inherit;
|
|
||||||
display: block;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
#mp ul,
|
|
||||||
#mp ol {
|
|
||||||
border-left: .3em solid #ddd;
|
|
||||||
}
|
|
||||||
#m>ul,
|
|
||||||
#m>ol {
|
|
||||||
border-color: #bbb;
|
|
||||||
}
|
|
||||||
#mp ul>li {
|
|
||||||
list-style-type: disc;
|
|
||||||
}
|
|
||||||
#mp ul>li,
|
|
||||||
#mp ol>li {
|
|
||||||
margin: .7em 0;
|
|
||||||
}
|
|
||||||
strong {
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
p>em,
|
|
||||||
li>em,
|
|
||||||
td>em {
|
|
||||||
color: #c50;
|
|
||||||
padding: .1em;
|
|
||||||
border-bottom: .1em solid #bbb;
|
|
||||||
}
|
|
||||||
blockquote {
|
|
||||||
font-family: serif;
|
|
||||||
background: #f7f7f7;
|
|
||||||
border: .07em dashed #ccc;
|
|
||||||
padding: 0 2em;
|
|
||||||
margin: 1em 0;
|
|
||||||
}
|
|
||||||
small {
|
|
||||||
opacity: .8;
|
|
||||||
}
|
}
|
||||||
#toc {
|
#toc {
|
||||||
margin: 0 1em;
|
margin: 0 1em;
|
||||||
@@ -182,14 +72,6 @@ small {
|
|||||||
color: #6b3;
|
color: #6b3;
|
||||||
text-shadow: .02em 0 0 #6b3;
|
text-shadow: .02em 0 0 #6b3;
|
||||||
}
|
}
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin: 1em 0;
|
|
||||||
}
|
|
||||||
th, td {
|
|
||||||
padding: .2em .5em;
|
|
||||||
border: .12em solid #aaa;
|
|
||||||
}
|
|
||||||
blink {
|
blink {
|
||||||
animation: blinker .7s cubic-bezier(.9, 0, .1, 1) infinite;
|
animation: blinker .7s cubic-bezier(.9, 0, .1, 1) infinite;
|
||||||
}
|
}
|
||||||
@@ -202,6 +84,36 @@ blink {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.mdo pre {
|
||||||
|
counter-reset: precode;
|
||||||
|
}
|
||||||
|
.mdo pre code {
|
||||||
|
counter-increment: precode;
|
||||||
|
display: inline-block;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid #cdc;
|
||||||
|
min-width: calc(100% - .6em);
|
||||||
|
}
|
||||||
|
.mdo pre code:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.mdo pre code::before {
|
||||||
|
content: counter(precode);
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: right;
|
||||||
|
font-size: .75em;
|
||||||
|
color: #48a;
|
||||||
|
width: 4em;
|
||||||
|
padding-right: 1.5em;
|
||||||
|
margin-left: -5.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@media screen {
|
@media screen {
|
||||||
html, body {
|
html, body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -218,34 +130,6 @@ blink {
|
|||||||
#mp {
|
#mp {
|
||||||
max-width: 52em;
|
max-width: 52em;
|
||||||
margin-bottom: 6em;
|
margin-bottom: 6em;
|
||||||
word-break: break-word;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
word-wrap: break-word; /*ie*/
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #fff;
|
|
||||||
background: #39b;
|
|
||||||
text-decoration: none;
|
|
||||||
padding: 0 .3em;
|
|
||||||
border: none;
|
|
||||||
border-bottom: .07em solid #079;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
color: #fff;
|
|
||||||
background: #555;
|
|
||||||
margin-top: 2em;
|
|
||||||
border-bottom: .22em solid #999;
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
color: #fff;
|
|
||||||
background: #444;
|
|
||||||
font-weight: normal;
|
|
||||||
border-top: .4em solid #fb0;
|
|
||||||
border-bottom: .4em solid #777;
|
|
||||||
border-radius: 0 1em 0 1em;
|
|
||||||
margin: 3em 0 1em 0;
|
|
||||||
padding: .5em 0;
|
|
||||||
}
|
}
|
||||||
#mn {
|
#mn {
|
||||||
padding: 1.3em 0 .7em 1em;
|
padding: 1.3em 0 .7em 1em;
|
||||||
@@ -298,6 +182,8 @@ blink {
|
|||||||
color: #444;
|
color: #444;
|
||||||
background: none;
|
background: none;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
margin: 0 .1em;
|
||||||
|
padding: 0 .3em;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
#mh a:hover {
|
#mh a:hover {
|
||||||
@@ -346,55 +232,6 @@ blink {
|
|||||||
html.dark #toc li {
|
html.dark #toc li {
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
}
|
}
|
||||||
html.dark #mp a {
|
|
||||||
background: #057;
|
|
||||||
}
|
|
||||||
html.dark #mp h1 a, html.dark #mp h4 a,
|
|
||||||
html.dark #mp h2 a, html.dark #mp h5 a,
|
|
||||||
html.dark #mp h3 a, html.dark #mp h6 a {
|
|
||||||
color: inherit;
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
html.dark pre,
|
|
||||||
html.dark code {
|
|
||||||
color: #8c0;
|
|
||||||
background: #1a1a1a;
|
|
||||||
border: .07em solid #333;
|
|
||||||
}
|
|
||||||
html.dark #mp ul,
|
|
||||||
html.dark #mp ol {
|
|
||||||
border-color: #444;
|
|
||||||
}
|
|
||||||
html.dark #m>ul,
|
|
||||||
html.dark #m>ol {
|
|
||||||
border-color: #555;
|
|
||||||
}
|
|
||||||
html.dark strong {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
html.dark p>em,
|
|
||||||
html.dark li>em,
|
|
||||||
html.dark td>em {
|
|
||||||
color: #f94;
|
|
||||||
border-color: #666;
|
|
||||||
}
|
|
||||||
html.dark h1 {
|
|
||||||
background: #383838;
|
|
||||||
border-top: .4em solid #b80;
|
|
||||||
border-bottom: .4em solid #4c4c4c;
|
|
||||||
}
|
|
||||||
html.dark h2 {
|
|
||||||
background: #444;
|
|
||||||
border-bottom: .22em solid #555;
|
|
||||||
}
|
|
||||||
html.dark td,
|
|
||||||
html.dark th {
|
|
||||||
border-color: #444;
|
|
||||||
}
|
|
||||||
html.dark blockquote {
|
|
||||||
background: #282828;
|
|
||||||
border: .07em dashed #444;
|
|
||||||
}
|
|
||||||
html.dark #mn a:not(:last-child)::after {
|
html.dark #mn a:not(:last-child)::after {
|
||||||
border-color: rgba(255,255,255,0.3);
|
border-color: rgba(255,255,255,0.3);
|
||||||
}
|
}
|
||||||
@@ -500,12 +337,15 @@ blink {
|
|||||||
mso-footer-margin: .6in;
|
mso-footer-margin: .6in;
|
||||||
mso-paper-source: 0;
|
mso-paper-source: 0;
|
||||||
}
|
}
|
||||||
a {
|
.mdo a {
|
||||||
color: #079;
|
color: #079;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border-bottom: .07em solid #4ac;
|
border-bottom: .07em solid #4ac;
|
||||||
padding: 0 .3em;
|
padding: 0 .3em;
|
||||||
}
|
}
|
||||||
|
#repl {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
#toc>ul {
|
#toc>ul {
|
||||||
border-left: .1em solid #84c4dd;
|
border-left: .1em solid #84c4dd;
|
||||||
}
|
}
|
||||||
@@ -530,18 +370,20 @@ blink {
|
|||||||
a[ctr]::before {
|
a[ctr]::before {
|
||||||
content: attr(ctr) '. ';
|
content: attr(ctr) '. ';
|
||||||
}
|
}
|
||||||
h1 {
|
.mdo h1 {
|
||||||
margin: 2em 0;
|
margin: 2em 0;
|
||||||
}
|
}
|
||||||
h2 {
|
.mdo h2 {
|
||||||
margin: 2em 0 0 0;
|
margin: 2em 0 0 0;
|
||||||
}
|
}
|
||||||
h1, h2, h3 {
|
.mdo h1,
|
||||||
|
.mdo h2,
|
||||||
|
.mdo h3 {
|
||||||
page-break-inside: avoid;
|
page-break-inside: avoid;
|
||||||
}
|
}
|
||||||
h1::after,
|
.mdo h1::after,
|
||||||
h2::after,
|
.mdo h2::after,
|
||||||
h3::after {
|
.mdo h3::after {
|
||||||
content: 'orz';
|
content: 'orz';
|
||||||
color: transparent;
|
color: transparent;
|
||||||
display: block;
|
display: block;
|
||||||
@@ -549,20 +391,20 @@ blink {
|
|||||||
padding: 4em 0 0 0;
|
padding: 4em 0 0 0;
|
||||||
margin: 0 0 -5em 0;
|
margin: 0 0 -5em 0;
|
||||||
}
|
}
|
||||||
p {
|
.mdo p {
|
||||||
page-break-inside: avoid;
|
page-break-inside: avoid;
|
||||||
}
|
}
|
||||||
table {
|
.mdo table {
|
||||||
page-break-inside: auto;
|
page-break-inside: auto;
|
||||||
}
|
}
|
||||||
tr {
|
.mdo tr {
|
||||||
page-break-inside: avoid;
|
page-break-inside: avoid;
|
||||||
page-break-after: auto;
|
page-break-after: auto;
|
||||||
}
|
}
|
||||||
thead {
|
.mdo thead {
|
||||||
display: table-header-group;
|
display: table-header-group;
|
||||||
}
|
}
|
||||||
tfoot {
|
.mdo tfoot {
|
||||||
display: table-footer-group;
|
display: table-footer-group;
|
||||||
}
|
}
|
||||||
#mp a.vis::after {
|
#mp a.vis::after {
|
||||||
@@ -570,31 +412,32 @@ blink {
|
|||||||
border-bottom: 1px solid #bbb;
|
border-bottom: 1px solid #bbb;
|
||||||
color: #444;
|
color: #444;
|
||||||
}
|
}
|
||||||
blockquote {
|
.mdo blockquote {
|
||||||
border-color: #555;
|
border-color: #555;
|
||||||
}
|
}
|
||||||
code {
|
.mdo code {
|
||||||
border-color: #bbb;
|
border-color: #bbb;
|
||||||
}
|
}
|
||||||
pre, pre code {
|
.mdo pre,
|
||||||
|
.mdo pre code {
|
||||||
border-color: #999;
|
border-color: #999;
|
||||||
}
|
}
|
||||||
pre code::before {
|
.mdo pre code::before {
|
||||||
color: #058;
|
color: #058;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
html.dark a {
|
html.dark .mdo a {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
html.dark pre,
|
html.dark .mdo pre,
|
||||||
html.dark code {
|
html.dark .mdo code {
|
||||||
color: #240;
|
color: #240;
|
||||||
}
|
}
|
||||||
html.dark p>em,
|
html.dark .mdo p>em,
|
||||||
html.dark li>em,
|
html.dark .mdo li>em,
|
||||||
html.dark td>em {
|
html.dark .mdo td>em {
|
||||||
color: #940;
|
color: #940;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,9 @@
|
|||||||
if you're still reading this, check that javascript is allowed
|
if you're still reading this, check that javascript is allowed
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="mp"></div>
|
<div id="mp" class="mdo"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<a href="#" id="repl">π</a>
|
||||||
|
|
||||||
{%- if edit %}
|
{%- if edit %}
|
||||||
<div id="helpbox">
|
<div id="helpbox">
|
||||||
|
|||||||
@@ -24,23 +24,6 @@ var dbg = function () { };
|
|||||||
var md_plug = {};
|
var md_plug = {};
|
||||||
|
|
||||||
|
|
||||||
function hesc(txt) {
|
|
||||||
return txt.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function cls(dom, name, add) {
|
|
||||||
var re = new RegExp('(^| )' + name + '( |$)');
|
|
||||||
var lst = (dom.getAttribute('class') + '').replace(re, "$1$2").replace(/ /, "");
|
|
||||||
dom.setAttribute('class', lst + (add ? ' ' + name : ''));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function statify(obj) {
|
|
||||||
return JSON.parse(JSON.stringify(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// dodge browser issues
|
// dodge browser issues
|
||||||
(function () {
|
(function () {
|
||||||
var ua = navigator.userAgent;
|
var ua = navigator.userAgent;
|
||||||
@@ -65,7 +48,7 @@ function statify(obj) {
|
|||||||
if (a > 0)
|
if (a > 0)
|
||||||
loc.push(n[a]);
|
loc.push(n[a]);
|
||||||
|
|
||||||
var dec = hesc(uricom_dec(n[a])[0]);
|
var dec = esc(uricom_dec(n[a])[0]);
|
||||||
|
|
||||||
nav.push('<a href="/' + loc.join('/') + '">' + dec + '</a>');
|
nav.push('<a href="/' + loc.join('/') + '">' + dec + '</a>');
|
||||||
}
|
}
|
||||||
@@ -73,6 +56,26 @@ function statify(obj) {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
// image load handler
|
||||||
|
var img_load = (function () {
|
||||||
|
var r = {};
|
||||||
|
r.callbacks = [];
|
||||||
|
|
||||||
|
function fire() {
|
||||||
|
for (var a = 0; a < r.callbacks.length; a++)
|
||||||
|
r.callbacks[a]();
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeout = null;
|
||||||
|
r.done = function () {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(fire, 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
return r;
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
// faster than replacing the entire html (chrome 1.8x, firefox 1.6x)
|
// faster than replacing the entire html (chrome 1.8x, firefox 1.6x)
|
||||||
function copydom(src, dst, lv) {
|
function copydom(src, dst, lv) {
|
||||||
var sc = src.childNodes,
|
var sc = src.childNodes,
|
||||||
@@ -356,6 +359,10 @@ function convert_markdown(md_text, dest_dom) {
|
|||||||
|
|
||||||
copydom(md_dom, dest_dom, 0);
|
copydom(md_dom, dest_dom, 0);
|
||||||
|
|
||||||
|
var imgs = dest_dom.getElementsByTagName('img');
|
||||||
|
for (var a = 0, aa = imgs.length; a < aa; a++)
|
||||||
|
imgs[a].onload = img_load.done;
|
||||||
|
|
||||||
if (ext && ext[0].render2)
|
if (ext && ext[0].render2)
|
||||||
try {
|
try {
|
||||||
ext[0].render2(dest_dom);
|
ext[0].render2(dest_dom);
|
||||||
@@ -490,6 +497,7 @@ function init_toc() {
|
|||||||
// "main" :p
|
// "main" :p
|
||||||
convert_markdown(dom_src.value, dom_pre);
|
convert_markdown(dom_src.value, dom_pre);
|
||||||
var toc = init_toc();
|
var toc = init_toc();
|
||||||
|
img_load.callbacks = [toc.refresh];
|
||||||
|
|
||||||
|
|
||||||
// scroll handler
|
// scroll handler
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: 'consolas', monospace, monospace;
|
font-family: 'scp', monospace, monospace;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ var draw_md = (function () {
|
|||||||
var src = dom_src.value;
|
var src = dom_src.value;
|
||||||
convert_markdown(src, dom_pre);
|
convert_markdown(src, dom_pre);
|
||||||
|
|
||||||
var lines = hesc(src).replace(/\r/g, "").split('\n');
|
var lines = esc(src).replace(/\r/g, "").split('\n');
|
||||||
nlines = lines.length;
|
nlines = lines.length;
|
||||||
var html = [];
|
var html = [];
|
||||||
for (var a = 0; a < lines.length; a++)
|
for (var a = 0; a < lines.length; a++)
|
||||||
@@ -108,7 +108,7 @@ var draw_md = (function () {
|
|||||||
map_src = genmap(dom_ref, map_src);
|
map_src = genmap(dom_ref, map_src);
|
||||||
map_pre = genmap(dom_pre, map_pre);
|
map_pre = genmap(dom_pre, map_pre);
|
||||||
|
|
||||||
cls(ebi('save'), 'disabled', src == server_md);
|
clmod(ebi('save'), 'disabled', src == server_md);
|
||||||
|
|
||||||
var t1 = Date.now();
|
var t1 = Date.now();
|
||||||
delay = t1 - t0 > 100 ? 25 : 1;
|
delay = t1 - t0 > 100 ? 25 : 1;
|
||||||
@@ -127,6 +127,12 @@ var draw_md = (function () {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
// discard TOC callback, just regen editor scroll map
|
||||||
|
img_load.callbacks = [function () {
|
||||||
|
map_pre = genmap(dom_pre, map_pre);
|
||||||
|
}];
|
||||||
|
|
||||||
|
|
||||||
// resize handler
|
// resize handler
|
||||||
redraw = (function () {
|
redraw = (function () {
|
||||||
function onresize() {
|
function onresize() {
|
||||||
@@ -136,7 +142,6 @@ redraw = (function () {
|
|||||||
dom_ref.style.width = getComputedStyle(dom_src).offsetWidth + 'px';
|
dom_ref.style.width = getComputedStyle(dom_src).offsetWidth + 'px';
|
||||||
map_src = genmap(dom_ref, map_src);
|
map_src = genmap(dom_ref, map_src);
|
||||||
map_pre = genmap(dom_pre, map_pre);
|
map_pre = genmap(dom_pre, map_pre);
|
||||||
dbg(document.body.clientWidth + 'x' + document.body.clientHeight);
|
|
||||||
}
|
}
|
||||||
function setsbs() {
|
function setsbs() {
|
||||||
dom_wrap.setAttribute('class', '');
|
dom_wrap.setAttribute('class', '');
|
||||||
@@ -1086,9 +1091,9 @@ action_stack = (function () {
|
|||||||
ref = newtxt;
|
ref = newtxt;
|
||||||
dbg('undos(%d) redos(%d)', hist.un.length, hist.re.length);
|
dbg('undos(%d) redos(%d)', hist.un.length, hist.re.length);
|
||||||
if (hist.un.length > 0)
|
if (hist.un.length > 0)
|
||||||
dbg(statify(hist.un.slice(-1)[0]));
|
dbg(jcp(hist.un.slice(-1)[0]));
|
||||||
if (hist.re.length > 0)
|
if (hist.re.length > 0)
|
||||||
dbg(statify(hist.re.slice(-1)[0]));
|
dbg(jcp(hist.re.slice(-1)[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ html .editor-toolbar>button.active { border-color: rgba(0,0,0,0.4); background:
|
|||||||
html .editor-toolbar>i.separator { border-left: 1px solid #ccc; }
|
html .editor-toolbar>i.separator { border-left: 1px solid #ccc; }
|
||||||
html .editor-toolbar.disabled-for-preview>button:not(.no-disable) { opacity: .35 }
|
html .editor-toolbar.disabled-for-preview>button:not(.no-disable) { opacity: .35 }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
html {
|
html {
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
}
|
}
|
||||||
@@ -22,6 +24,18 @@ html, body {
|
|||||||
bottom: auto;
|
bottom: auto;
|
||||||
top: 1.4em;
|
top: 1.4em;
|
||||||
}
|
}
|
||||||
|
#repl {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: .5em;
|
||||||
|
border: none;
|
||||||
|
color: inherit;
|
||||||
|
background: none;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#mn {
|
#mn {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
margin: 1.3em 0 .7em 1em;
|
margin: 1.3em 0 .7em 1em;
|
||||||
@@ -63,148 +77,12 @@ html .editor-toolbar>button.disabled {
|
|||||||
html .editor-toolbar>button.save.force-save {
|
html .editor-toolbar>button.save.force-save {
|
||||||
background: #f97;
|
background: #f97;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* copied from md.css for now */
|
|
||||||
.mdo pre,
|
|
||||||
.mdo code,
|
|
||||||
.mdo a {
|
|
||||||
color: #480;
|
|
||||||
background: #f7f7f7;
|
|
||||||
border: .07em solid #ddd;
|
|
||||||
border-radius: .2em;
|
|
||||||
padding: .1em .3em;
|
|
||||||
margin: 0 .1em;
|
|
||||||
}
|
|
||||||
.mdo code {
|
|
||||||
font-size: .96em;
|
|
||||||
}
|
|
||||||
.mdo pre,
|
|
||||||
.mdo code {
|
|
||||||
font-family: monospace, monospace;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
.mdo pre code {
|
|
||||||
display: block;
|
|
||||||
margin: 0 -.3em;
|
|
||||||
padding: .4em .5em;
|
|
||||||
line-height: 1.1em;
|
|
||||||
}
|
|
||||||
.mdo a {
|
|
||||||
color: #fff;
|
|
||||||
background: #39b;
|
|
||||||
text-decoration: none;
|
|
||||||
padding: 0 .3em;
|
|
||||||
border: none;
|
|
||||||
border-bottom: .07em solid #079;
|
|
||||||
}
|
|
||||||
.mdo h2 {
|
|
||||||
color: #fff;
|
|
||||||
background: #555;
|
|
||||||
margin-top: 2em;
|
|
||||||
border-bottom: .22em solid #999;
|
|
||||||
border-top: none;
|
|
||||||
}
|
|
||||||
.mdo h1 {
|
|
||||||
color: #fff;
|
|
||||||
background: #444;
|
|
||||||
font-weight: normal;
|
|
||||||
border-top: .4em solid #fb0;
|
|
||||||
border-bottom: .4em solid #777;
|
|
||||||
border-radius: 0 1em 0 1em;
|
|
||||||
margin: 3em 0 1em 0;
|
|
||||||
padding: .5em 0;
|
|
||||||
}
|
|
||||||
h1, h2 {
|
|
||||||
line-height: 1.5em;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 1.7em;
|
|
||||||
text-align: center;
|
|
||||||
border: 1em solid #777;
|
|
||||||
border-width: .05em 0;
|
|
||||||
margin: 3em 0;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: 1.5em;
|
|
||||||
font-weight: normal;
|
|
||||||
background: #f7f7f7;
|
|
||||||
border-top: .07em solid #fff;
|
|
||||||
border-bottom: .07em solid #bbb;
|
|
||||||
border-radius: .5em .5em 0 0;
|
|
||||||
padding-left: .4em;
|
|
||||||
margin-top: 3em;
|
|
||||||
}
|
|
||||||
.mdo ul,
|
|
||||||
.mdo ol {
|
|
||||||
border-left: .3em solid #ddd;
|
|
||||||
}
|
|
||||||
.mdo>ul,
|
|
||||||
.mdo>ol {
|
|
||||||
border-color: #bbb;
|
|
||||||
}
|
|
||||||
.mdo ul>li {
|
|
||||||
list-style-type: disc;
|
|
||||||
}
|
|
||||||
.mdo ul>li,
|
|
||||||
.mdo ol>li {
|
|
||||||
margin: .7em 0;
|
|
||||||
}
|
|
||||||
strong {
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
p>em,
|
|
||||||
li>em,
|
|
||||||
td>em {
|
|
||||||
color: #c50;
|
|
||||||
padding: .1em;
|
|
||||||
border-bottom: .1em solid #bbb;
|
|
||||||
}
|
|
||||||
blockquote {
|
|
||||||
font-family: serif;
|
|
||||||
background: #f7f7f7;
|
|
||||||
border: .07em dashed #ccc;
|
|
||||||
padding: 0 2em;
|
|
||||||
margin: 1em 0;
|
|
||||||
}
|
|
||||||
small {
|
|
||||||
opacity: .8;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
td {
|
|
||||||
padding: .2em .5em;
|
|
||||||
border: .12em solid #aaa;
|
|
||||||
}
|
|
||||||
th {
|
|
||||||
border: .12em solid #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* mde support */
|
|
||||||
.mdo {
|
|
||||||
padding: 1em;
|
|
||||||
background: #f7f7f7;
|
|
||||||
}
|
|
||||||
html.dark .mdo {
|
|
||||||
background: #1c1c1c;
|
|
||||||
}
|
|
||||||
.CodeMirror {
|
.CodeMirror {
|
||||||
background: #f7f7f7;
|
background: #f7f7f7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* darkmode */
|
/* darkmode */
|
||||||
html.dark .mdo,
|
html.dark .mdo,
|
||||||
html.dark .CodeMirror {
|
html.dark .CodeMirror {
|
||||||
@@ -228,55 +106,6 @@ html.dark .CodeMirror-selectedtext {
|
|||||||
background: #246;
|
background: #246;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
html.dark .mdo a {
|
|
||||||
background: #057;
|
|
||||||
}
|
|
||||||
html.dark .mdo h1 a, html.dark .mdo h4 a,
|
|
||||||
html.dark .mdo h2 a, html.dark .mdo h5 a,
|
|
||||||
html.dark .mdo h3 a, html.dark .mdo h6 a {
|
|
||||||
color: inherit;
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
html.dark pre,
|
|
||||||
html.dark code {
|
|
||||||
color: #8c0;
|
|
||||||
background: #1a1a1a;
|
|
||||||
border: .07em solid #333;
|
|
||||||
}
|
|
||||||
html.dark .mdo ul,
|
|
||||||
html.dark .mdo ol {
|
|
||||||
border-color: #444;
|
|
||||||
}
|
|
||||||
html.dark .mdo>ul,
|
|
||||||
html.dark .mdo>ol {
|
|
||||||
border-color: #555;
|
|
||||||
}
|
|
||||||
html.dark strong {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
html.dark p>em,
|
|
||||||
html.dark li>em,
|
|
||||||
html.dark td>em {
|
|
||||||
color: #f94;
|
|
||||||
border-color: #666;
|
|
||||||
}
|
|
||||||
html.dark h1 {
|
|
||||||
background: #383838;
|
|
||||||
border-top: .4em solid #b80;
|
|
||||||
border-bottom: .4em solid #4c4c4c;
|
|
||||||
}
|
|
||||||
html.dark h2 {
|
|
||||||
background: #444;
|
|
||||||
border-bottom: .22em solid #555;
|
|
||||||
}
|
|
||||||
html.dark td,
|
|
||||||
html.dark th {
|
|
||||||
border-color: #444;
|
|
||||||
}
|
|
||||||
html.dark blockquote {
|
|
||||||
background: #282828;
|
|
||||||
border: .07em dashed #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -313,3 +142,14 @@ html.dark .editor-toolbar::after,
|
|||||||
html.dark .editor-toolbar::before {
|
html.dark .editor-toolbar::before {
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ui.css overrides */
|
||||||
|
.mdo {
|
||||||
|
padding: 1em;
|
||||||
|
background: #f7f7f7;
|
||||||
|
}
|
||||||
|
html.dark .mdo {
|
||||||
|
background: #1c1c1c;
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
<textarea id="mt" style="display:none" autocomplete="off">{{ md }}</textarea>
|
<textarea id="mt" style="display:none" autocomplete="off">{{ md }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<a href="#" id="repl">π</a>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
var last_modified = {{ lastmod }};
|
var last_modified = {{ lastmod }};
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ a {
|
|||||||
border-radius: .2em;
|
border-radius: .2em;
|
||||||
padding: .2em .8em;
|
padding: .2em .8em;
|
||||||
}
|
}
|
||||||
|
#repl {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
color: inherit;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
table {
|
table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
||||||
<link rel="stylesheet" media="screen" href="/.cpr/splash.css?_={{ ts }}">
|
<link rel="stylesheet" media="screen" href="/.cpr/splash.css?_={{ ts }}">
|
||||||
|
<link rel="stylesheet" media="screen" href="/.cpr/ui.css?_={{ ts }}">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -66,11 +67,13 @@
|
|||||||
</form>
|
</form>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<a href="#" id="repl">π</a>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
if (localStorage.getItem('lightmode') != 1)
|
if (localStorage.getItem('lightmode') != 1)
|
||||||
document.documentElement.setAttribute("class", "dark");
|
document.documentElement.setAttribute("class", "dark");
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
<script src="/.cpr/util.js?_={{ ts }}"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'scp';
|
||||||
|
src: local('Source Code Pro Regular'), local('SourceCodePro-Regular'), url(/.cpr/deps/scp.woff2) format('woff2');
|
||||||
|
}
|
||||||
html {
|
html {
|
||||||
touch-action: manipulation;
|
touch-action: manipulation;
|
||||||
}
|
}
|
||||||
@@ -26,7 +30,9 @@ html {
|
|||||||
right: -1em;
|
right: -1em;
|
||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
padding: 1em 1.3em;
|
padding: 1em 1.3em;
|
||||||
|
margin-left: 3em;
|
||||||
border-width: .4em 0;
|
border-width: .4em 0;
|
||||||
|
overflow-wrap: break-word;
|
||||||
transform: translateX(100%);
|
transform: translateX(100%);
|
||||||
transition:
|
transition:
|
||||||
transform .4s cubic-bezier(.2, 1.2, .5, 1),
|
transform .4s cubic-bezier(.2, 1.2, .5, 1),
|
||||||
@@ -34,7 +40,14 @@ html {
|
|||||||
text-shadow: 1px 1px 0 #000;
|
text-shadow: 1px 1px 0 #000;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
#toastc {
|
#toast a {
|
||||||
|
color: inherit;
|
||||||
|
text-shadow: inherit;
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
border-radius: .3em;
|
||||||
|
padding: .2em .3em;
|
||||||
|
}
|
||||||
|
#toast a#toastc {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -43,7 +56,7 @@ html {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
padding: .3em 0;
|
padding: .3em 0;
|
||||||
margin: -.3em 0 0 0;
|
margin: -.3em 0 0 0;
|
||||||
line-height: 1.5em;
|
line-height: 1.3em;
|
||||||
color: #000;
|
color: #000;
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -51,6 +64,15 @@ html {
|
|||||||
border-radius: .5em 0 0 .5em;
|
border-radius: .5em 0 0 .5em;
|
||||||
transition: left .3s, width .3s, padding .3s, opacity .3s;
|
transition: left .3s, width .3s, padding .3s, opacity .3s;
|
||||||
}
|
}
|
||||||
|
#toastb {
|
||||||
|
max-height: 70vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
#toast.scroll #toastb {
|
||||||
|
overflow-y: scroll;
|
||||||
|
margin-right: -1.2em;
|
||||||
|
padding-right: .7em;
|
||||||
|
}
|
||||||
#toast pre {
|
#toast pre {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
@@ -107,6 +129,7 @@ html {
|
|||||||
padding: 1.5em 2em;
|
padding: 1.5em 2em;
|
||||||
border-width: .5em 0;
|
border-width: .5em 0;
|
||||||
}
|
}
|
||||||
|
#modalc code,
|
||||||
#tt code {
|
#tt code {
|
||||||
background: #3c3c3c;
|
background: #3c3c3c;
|
||||||
padding: .1em .3em;
|
padding: .1em .3em;
|
||||||
@@ -125,6 +148,7 @@ html.light #tt,
|
|||||||
html.light #toast {
|
html.light #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.light #tt code {
|
html.light #tt code {
|
||||||
background: #060;
|
background: #060;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
@@ -207,6 +231,8 @@ html.light #tt em {
|
|||||||
}
|
}
|
||||||
#modali {
|
#modali {
|
||||||
display: block;
|
display: block;
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
width: calc(100% - 1.25em);
|
width: calc(100% - 1.25em);
|
||||||
margin: 1em -.1em 0 -.1em;
|
margin: 1em -.1em 0 -.1em;
|
||||||
padding: .5em;
|
padding: .5em;
|
||||||
@@ -217,3 +243,200 @@ html.light #tt em {
|
|||||||
#modali:focus {
|
#modali:focus {
|
||||||
border-color: #06d;
|
border-color: #06d;
|
||||||
}
|
}
|
||||||
|
#repl_pre {
|
||||||
|
max-width: 24em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.mdo pre,
|
||||||
|
.mdo code,
|
||||||
|
.mdo a {
|
||||||
|
color: #480;
|
||||||
|
background: #f7f7f7;
|
||||||
|
border: .07em solid #ddd;
|
||||||
|
border-radius: .2em;
|
||||||
|
padding: .1em .3em;
|
||||||
|
margin: 0 .1em;
|
||||||
|
}
|
||||||
|
.mdo pre,
|
||||||
|
.mdo code,
|
||||||
|
.mdo tt {
|
||||||
|
font-family: 'scp', monospace, monospace;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.mdo code {
|
||||||
|
font-size: .96em;
|
||||||
|
}
|
||||||
|
.mdo h1,
|
||||||
|
.mdo h2 {
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
.mdo h1 {
|
||||||
|
font-size: 1.7em;
|
||||||
|
text-align: center;
|
||||||
|
border: 1em solid #777;
|
||||||
|
border-width: .05em 0;
|
||||||
|
margin: 3em 0;
|
||||||
|
}
|
||||||
|
.mdo h2 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
font-weight: normal;
|
||||||
|
background: #f7f7f7;
|
||||||
|
border-top: .07em solid #fff;
|
||||||
|
border-bottom: .07em solid #bbb;
|
||||||
|
border-radius: .5em .5em 0 0;
|
||||||
|
padding-left: .4em;
|
||||||
|
margin-top: 3em;
|
||||||
|
}
|
||||||
|
.mdo h3 {
|
||||||
|
border-bottom: .1em solid #999;
|
||||||
|
}
|
||||||
|
.mdo h1 a, .mdo h3 a, .mdo h5 a,
|
||||||
|
.mdo h2 a, .mdo h4 a, .mdo h6 a {
|
||||||
|
color: inherit;
|
||||||
|
display: block;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.mdo ul,
|
||||||
|
.mdo ol {
|
||||||
|
border-left: .3em solid #ddd;
|
||||||
|
}
|
||||||
|
.mdo ul>li,
|
||||||
|
.mdo ol>li {
|
||||||
|
margin: .7em 0;
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
.mdo strong {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
.mdo p>em,
|
||||||
|
.mdo li>em,
|
||||||
|
.mdo td>em {
|
||||||
|
color: #c50;
|
||||||
|
padding: .1em;
|
||||||
|
border-bottom: .1em solid #bbb;
|
||||||
|
}
|
||||||
|
.mdo blockquote {
|
||||||
|
font-family: serif;
|
||||||
|
background: #f7f7f7;
|
||||||
|
border: .07em dashed #ccc;
|
||||||
|
padding: 0 2em;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
.mdo small {
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
.mdo pre code {
|
||||||
|
display: block;
|
||||||
|
margin: 0 -.3em;
|
||||||
|
padding: .4em .5em;
|
||||||
|
line-height: 1.1em;
|
||||||
|
}
|
||||||
|
.mdo pre code:hover {
|
||||||
|
background: #fec;
|
||||||
|
color: #360;
|
||||||
|
}
|
||||||
|
.mdo table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
.mdo th,
|
||||||
|
.mdo td {
|
||||||
|
padding: .2em .5em;
|
||||||
|
border: .12em solid #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen {
|
||||||
|
.mdo {
|
||||||
|
word-break: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-wrap: break-word; /*ie*/
|
||||||
|
}
|
||||||
|
html.light .mdo a,
|
||||||
|
.mdo a {
|
||||||
|
color: #fff;
|
||||||
|
background: #39b;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0 .3em;
|
||||||
|
border: none;
|
||||||
|
border-bottom: .07em solid #079;
|
||||||
|
}
|
||||||
|
.mdo h1 {
|
||||||
|
color: #fff;
|
||||||
|
background: #444;
|
||||||
|
font-weight: normal;
|
||||||
|
border-top: .4em solid #fb0;
|
||||||
|
border-bottom: .4em solid #777;
|
||||||
|
border-radius: 0 1em 0 1em;
|
||||||
|
margin: 3em 0 1em 0;
|
||||||
|
padding: .5em 0;
|
||||||
|
}
|
||||||
|
.mdo h2 {
|
||||||
|
color: #fff;
|
||||||
|
background: #555;
|
||||||
|
margin-top: 2em;
|
||||||
|
border-bottom: .22em solid #999;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
html.dark .mdo a {
|
||||||
|
background: #057;
|
||||||
|
}
|
||||||
|
html.dark .mdo h1 a, html.dark .mdo h4 a,
|
||||||
|
html.dark .mdo h2 a, html.dark .mdo h5 a,
|
||||||
|
html.dark .mdo h3 a, html.dark .mdo h6 a {
|
||||||
|
color: inherit;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
html.dark .mdo pre,
|
||||||
|
html.dark .mdo code {
|
||||||
|
color: #8c0;
|
||||||
|
background: #1a1a1a;
|
||||||
|
border: .07em solid #333;
|
||||||
|
}
|
||||||
|
html.dark .mdo ul,
|
||||||
|
html.dark .mdo ol {
|
||||||
|
border-color: #444;
|
||||||
|
}
|
||||||
|
html.dark .mdo strong {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
html.dark .mdo p>em,
|
||||||
|
html.dark .mdo li>em,
|
||||||
|
html.dark .mdo td>em {
|
||||||
|
color: #f94;
|
||||||
|
border-color: #666;
|
||||||
|
}
|
||||||
|
html.dark .mdo h1 {
|
||||||
|
background: #383838;
|
||||||
|
border-top: .4em solid #b80;
|
||||||
|
border-bottom: .4em solid #4c4c4c;
|
||||||
|
}
|
||||||
|
html.dark .mdo h2 {
|
||||||
|
background: #444;
|
||||||
|
border-bottom: .22em solid #555;
|
||||||
|
}
|
||||||
|
html.dark .mdo td,
|
||||||
|
html.dark .mdo th {
|
||||||
|
border-color: #444;
|
||||||
|
}
|
||||||
|
html.dark .mdo blockquote {
|
||||||
|
background: #282828;
|
||||||
|
border: .07em dashed #444;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -800,6 +800,12 @@ function up2k_init(subtle) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
good_files.sort(function (a, b) {
|
||||||
|
a = a[1];
|
||||||
|
b = b[1];
|
||||||
|
return a < b ? -1 : a > b ? 1 : 0;
|
||||||
|
});
|
||||||
|
|
||||||
var msg = ['{0} these {1} files?<ul>'.format(fsearch ? 'search' : 'upload', good_files.length)];
|
var msg = ['{0} these {1} files?<ul>'.format(fsearch ? 'search' : 'upload', good_files.length)];
|
||||||
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>');
|
||||||
@@ -898,7 +904,7 @@ function up2k_init(subtle) {
|
|||||||
}
|
}
|
||||||
ebi('u2cleanup').onclick = u2cleanup;
|
ebi('u2cleanup').onclick = u2cleanup;
|
||||||
|
|
||||||
var etaref = 0, etaskip = 0;
|
var etaref = 0, etaskip = 0, op_minh = 0;
|
||||||
function etafun() {
|
function etafun() {
|
||||||
var nhash = st.busy.head.length + st.busy.hash.length + st.todo.head.length + st.todo.hash.length,
|
var nhash = st.busy.head.length + st.busy.hash.length + st.todo.head.length + st.todo.hash.length,
|
||||||
nsend = st.busy.upload.length + st.todo.upload.length,
|
nsend = st.busy.upload.length + st.todo.upload.length,
|
||||||
@@ -906,7 +912,19 @@ function up2k_init(subtle) {
|
|||||||
td = (now - (etaref || now)) / 1000.0;
|
td = (now - (etaref || now)) / 1000.0;
|
||||||
|
|
||||||
etaref = now;
|
etaref = now;
|
||||||
//ebi('acc_info').innerHTML = f2f(st.time.busy, 1) + ' ' + f2f(now / 1000, 1);
|
if (td > 1.2)
|
||||||
|
td = 0.05;
|
||||||
|
|
||||||
|
//ebi('acc_info').innerHTML = humantime(st.time.busy) + ' ' + f2f(now / 1000, 1);
|
||||||
|
|
||||||
|
var op = ebi('op_up2k'),
|
||||||
|
uff = ebi('u2footfoot'),
|
||||||
|
minh = QS('#op_up2k.act') ? Math.max(op_minh, uff.offsetTop + uff.offsetHeight - op.offsetTop + 32) : 0;
|
||||||
|
|
||||||
|
if (minh > op_minh || !op_minh) {
|
||||||
|
op_minh = minh;
|
||||||
|
op.style.minHeight = op_minh + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
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 = 'Done ({0}, {1} files)'.format(humansize(st.bytes.hashed), pvis.ctr["ok"] + pvis.ctr["ng"]);
|
||||||
@@ -954,14 +972,8 @@ function up2k_init(subtle) {
|
|||||||
if (etaskip)
|
if (etaskip)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (eta < 60 * 60 * 24)
|
|
||||||
try {
|
|
||||||
eta = /.*(..:..:..).*/.exec(new Date(eta * 1000).toUTCString())[1];
|
|
||||||
}
|
|
||||||
catch (ex) { }
|
|
||||||
|
|
||||||
ebi(t[a][0]).innerHTML = '{0}, {1}/s, {2}'.format(
|
ebi(t[a][0]).innerHTML = '{0}, {1}/s, {2}'.format(
|
||||||
humansize(rem), humansize(bps, 1), eta);
|
humansize(rem), humansize(bps, 1), humantime(eta));
|
||||||
}
|
}
|
||||||
if (++etaskip > 2)
|
if (++etaskip > 2)
|
||||||
etaskip = 0;
|
etaskip = 0;
|
||||||
@@ -1075,6 +1087,7 @@ function up2k_init(subtle) {
|
|||||||
toast.err(t, '{0} {1}'.format(ks, tng));
|
toast.err(t, '{0} {1}'.format(ks, tng));
|
||||||
|
|
||||||
timer.rm(etafun);
|
timer.rm(etafun);
|
||||||
|
op_minh = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
timer.add(etafun, false);
|
timer.add(etafun, false);
|
||||||
@@ -1370,8 +1383,11 @@ function up2k_init(subtle) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
apop(st.busy.head, t);
|
apop(st.busy.head, t);
|
||||||
if (!ok && !t.srch)
|
if (!ok && !t.srch) {
|
||||||
return push_t(st.todo.hash, t);
|
push_t(st.todo.hash, t);
|
||||||
|
tasker();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
t.done = true;
|
t.done = true;
|
||||||
st.bytes.hashed += t.size;
|
st.bytes.hashed += t.size;
|
||||||
@@ -1380,6 +1396,7 @@ function up2k_init(subtle) {
|
|||||||
pvis.seth(t.n, 1, ok ? 'YOLO' : '404');
|
pvis.seth(t.n, 1, ok ? 'YOLO' : '404');
|
||||||
pvis.seth(t.n, 2, "turbo'd");
|
pvis.seth(t.n, 2, "turbo'd");
|
||||||
pvis.move(t.n, ok ? 'ok' : 'ng');
|
pvis.move(t.n, ok ? 'ok' : 'ng');
|
||||||
|
tasker();
|
||||||
};
|
};
|
||||||
xhr.onload = function (e) {
|
xhr.onload = function (e) {
|
||||||
try { orz(e); } catch (ex) { vis_exh(ex + '', '', '', '', ex); }
|
try { orz(e); } catch (ex) { vis_exh(ex + '', '', '', '', ex); }
|
||||||
@@ -1852,7 +1869,7 @@ function up2k_init(subtle) {
|
|||||||
flag = up2k_flagbus();
|
flag = up2k_flagbus();
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
console.log("flag error: " + ex.toString());
|
toast.err(5, "not supported on your browser:\n" + ex);
|
||||||
tgl_flag_en();
|
tgl_flag_en();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1880,17 +1897,18 @@ function up2k_init(subtle) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ebi('nthread').onkeydown = bumpthread2;
|
ebi('nthread').onkeydown = bumpthread2;
|
||||||
ebi('nthread').addEventListener('input', bumpthread, false);
|
ebi('nthread').oninput = bumpthread;
|
||||||
ebi('multitask').addEventListener('click', tgl_multitask, false);
|
ebi('multitask').onclick = tgl_multitask;
|
||||||
ebi('ask_up').addEventListener('click', tgl_ask_up, false);
|
ebi('ask_up').onclick = tgl_ask_up;
|
||||||
ebi('flag_en').addEventListener('click', tgl_flag_en, false);
|
ebi('flag_en').onclick = tgl_flag_en;
|
||||||
ebi('u2turbo').addEventListener('click', tgl_turbo, false);
|
ebi('u2turbo').onclick = tgl_turbo;
|
||||||
ebi('u2tdate').addEventListener('click', tgl_datechk, false);
|
ebi('u2tdate').onclick = tgl_datechk;
|
||||||
var o = ebi('fsearch');
|
var o = ebi('fsearch');
|
||||||
if (o)
|
if (o)
|
||||||
o.addEventListener('click', tgl_fsearch, false);
|
o.addEventListener('click', tgl_fsearch, false);
|
||||||
|
|
||||||
ebi('u2etas').onclick = function () {
|
ebi('u2etas').onclick = function (e) {
|
||||||
|
ev(e);
|
||||||
clmod(ebi('u2etas'), 'o', 't');
|
clmod(ebi('u2etas'), 'o', 't');
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1899,7 +1917,7 @@ function up2k_init(subtle) {
|
|||||||
if (parallel_uploads < 1)
|
if (parallel_uploads < 1)
|
||||||
bumpthread(1);
|
bumpthread(1);
|
||||||
|
|
||||||
return { "init_deps": init_deps, "set_fsearch": set_fsearch }
|
return { "init_deps": init_deps, "set_fsearch": set_fsearch, "ui": pvis }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ if (!window['console'])
|
|||||||
|
|
||||||
var is_touch = 'ontouchstart' in window,
|
var is_touch = 'ontouchstart' in window,
|
||||||
IPHONE = /iPhone|iPad|iPod/i.test(navigator.userAgent),
|
IPHONE = /iPhone|iPad|iPod/i.test(navigator.userAgent),
|
||||||
ANDROID = /android/i.test(navigator.userAgent);
|
ANDROID = /android/i.test(navigator.userAgent),
|
||||||
|
WINDOWS = navigator.platform ? navigator.platform == 'Win32' : /Windows/.test(navigator.userAgent);
|
||||||
|
|
||||||
|
|
||||||
var ebi = document.getElementById.bind(document),
|
var ebi = document.getElementById.bind(document),
|
||||||
@@ -28,6 +29,35 @@ function esc(txt) {
|
|||||||
}[c];
|
}[c];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
window.onunhandledrejection = function (e) {
|
||||||
|
console.log("REJ: " + e.reason);
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
console.hist = [];
|
||||||
|
var hook = function (t) {
|
||||||
|
var orig = console[t].bind(console),
|
||||||
|
cfun = function () {
|
||||||
|
console.hist.push(Date.now() + ' ' + t + ': ' + Array.from(arguments).join(', '));
|
||||||
|
if (console.hist.length > 100)
|
||||||
|
console.hist = console.hist.slice(50);
|
||||||
|
|
||||||
|
orig.apply(console, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
console['std' + t] = orig;
|
||||||
|
console[t] = cfun;
|
||||||
|
};
|
||||||
|
hook('log');
|
||||||
|
console.log('log-capture ok');
|
||||||
|
hook('debug');
|
||||||
|
hook('warn');
|
||||||
|
hook('error');
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
if (console.stdlog)
|
||||||
|
console.log = console.stdlog;
|
||||||
|
console.log(ex);
|
||||||
|
}
|
||||||
var crashed = false, ignexd = {};
|
var crashed = false, ignexd = {};
|
||||||
function vis_exh(msg, url, lineNo, columnNo, error) {
|
function vis_exh(msg, url, lineNo, columnNo, error) {
|
||||||
if ((msg + '').indexOf('ResizeObserver') !== -1)
|
if ((msg + '').indexOf('ResizeObserver') !== -1)
|
||||||
@@ -39,22 +69,59 @@ function vis_exh(msg, url, lineNo, columnNo, error) {
|
|||||||
|
|
||||||
crashed = true;
|
crashed = true;
|
||||||
window.onerror = undefined;
|
window.onerror = undefined;
|
||||||
var html = ['<h1>you hit a bug!</h1><p style="font-size:1.3em;margin:0">try to <a href="#" onclick="localStorage.clear();location.reload();">reset copyparty settings</a> if you are stuck here, or <a href="#" onclick="ignex();">ignore this</a> / <a href="#" onclick="ignex(true);">ignore all</a></p><p>please send me a screenshot arigathanks gozaimuch: <code>ed/irc.rizon.net</code> or <code>ed#2644</code><br /> (and if you can, press F12 and include the "Console" tab in the screenshot too)</p><p>',
|
var html = [
|
||||||
esc(url + ' @' + lineNo + ':' + columnNo), '<br />' + esc(String(msg)) + '</p>'];
|
'<h1>you hit a bug!</h1>',
|
||||||
|
'<p style="font-size:1.3em;margin:0">try to <a href="#" onclick="localStorage.clear();location.reload();">reset copyparty settings</a> if you are stuck here, or <a href="#" onclick="ignex();">ignore this</a> / <a href="#" onclick="ignex(true);">ignore all</a></p>',
|
||||||
|
'<p style="color:#fff">please send me a screenshot arigathanks gozaimuch: <code>ed/irc.rizon.net</code> or <code>ed#2644</code></p>',
|
||||||
|
'<p class="b">' + esc(url + ' @' + lineNo + ':' + columnNo), '<br />' + esc(String(msg)) + '</p>',
|
||||||
|
'<p><b>UA:</b> ' + esc(navigator.userAgent + '')
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
var ua = '',
|
||||||
|
ad = navigator.userAgentData,
|
||||||
|
adb = ad.brands;
|
||||||
|
|
||||||
|
for (var a = 0; a < adb.length; a++)
|
||||||
|
if (!/Not.*A.*Brand/.exec(adb[a].brand))
|
||||||
|
ua += adb[a].brand + '/' + adb[a].version + ', ';
|
||||||
|
ua += ad.platform;
|
||||||
|
|
||||||
|
html.push('<br /><b>UAD:</b> ' + esc(ua.slice(0, 100)));
|
||||||
|
}
|
||||||
|
catch (e) { }
|
||||||
|
html.push('</p>');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (error) {
|
if (error) {
|
||||||
var find = ['desc', 'stack', 'trace'];
|
var find = ['desc', 'stack', 'trace'];
|
||||||
for (var a = 0; a < find.length; a++)
|
for (var a = 0; a < find.length; a++)
|
||||||
if (String(error[find[a]]) !== 'undefined')
|
if (String(error[find[a]]) !== 'undefined')
|
||||||
html.push('<h3>' + find[a] + '</h3>' +
|
html.push('<p class="b"><b>' + find[a] + ':</b><br />' +
|
||||||
esc(String(error[find[a]])).replace(/\n/g, '<br />\n'));
|
esc(String(error[find[a]])).replace(/\n/g, '<br />\n') + '</p>');
|
||||||
}
|
}
|
||||||
ignexd[ekey] = true;
|
ignexd[ekey] = true;
|
||||||
html.push('<h3>localStore</h3>' + esc(JSON.stringify(localStorage)));
|
|
||||||
|
var ls = jcp(localStorage);
|
||||||
|
if (ls.fman_clip)
|
||||||
|
ls.fman_clip = ls.fman_clip.length + ' items';
|
||||||
|
|
||||||
|
var lsk = Object.keys(ls);
|
||||||
|
lsk.sort();
|
||||||
|
html.push('<p class="b">');
|
||||||
|
for (var a = 0; a < lsk.length; a++)
|
||||||
|
html.push(' <b>' + esc(lsk[a]) + '</b> <code>' + esc(ls[lsk[a]]) + '</code> ');
|
||||||
|
html.push('</p>');
|
||||||
}
|
}
|
||||||
catch (e) { }
|
catch (e) { }
|
||||||
|
|
||||||
|
if (console.hist.length) {
|
||||||
|
html.push('<p class="b"><b>console:</b><ul><li>' + Date.now() + ' @</li>');
|
||||||
|
for (var a = console.hist.length - 1, aa = Math.max(0, console.hist.length - 20); a >= aa; a--)
|
||||||
|
html.push('<li>' + esc(console.hist[a]) + '</li>');
|
||||||
|
html.push('</ul>')
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var exbox = ebi('exbox');
|
var exbox = ebi('exbox');
|
||||||
if (!exbox) {
|
if (!exbox) {
|
||||||
@@ -63,10 +130,19 @@ function vis_exh(msg, url, lineNo, columnNo, error) {
|
|||||||
document.body.appendChild(exbox);
|
document.body.appendChild(exbox);
|
||||||
|
|
||||||
var s = mknod('style');
|
var s = mknod('style');
|
||||||
s.innerHTML = '#exbox{background:#333;color:#ddd;font-family:sans-serif;font-size:0.8em;padding:0 1em 1em 1em;z-index:80386;position:fixed;top:0;left:0;right:0;bottom:0;width:100%;height:100%} #exbox h1{margin:.5em 1em 0 0;padding:0} #exbox h3{border-top:1px solid #999;margin:1em 0 0 0} #exbox a{text-decoration:underline;color:#fc0} #exbox code{color:#bf7;background:#222;padding:.1em;margin:.2em;font-size:1.1em;font-family:monospace,monospace} #exbox *{line-height:1.5em}';
|
s.innerHTML = (
|
||||||
|
'#exbox{background:#333;color:#ddd;font-family:sans-serif;font-size:0.8em;padding:0 1em 1em 1em;z-index:80386;position:fixed;top:0;left:0;right:0;bottom:0;width:100%;height:100%;overflow:auto;width:calc(100% - 2em)} ' +
|
||||||
|
'#exbox,#exbox *{line-height:1.5em;overflow-wrap:break-word} ' +
|
||||||
|
'#exbox code{color:#bf7;background:#222;padding:.1em;margin:.2em;font-size:1.1em;font-family:monospace,monospace} ' +
|
||||||
|
'#exbox a{text-decoration:underline;color:#fc0} ' +
|
||||||
|
'#exbox h1{margin:.5em 1em 0 0;padding:0} ' +
|
||||||
|
'#exbox p.b{border-top:1px solid #999;margin:1em 0 0 0;font-size:1em} ' +
|
||||||
|
'#exbox ul, #exbox li {margin:0 0 0 .5em;padding:0} ' +
|
||||||
|
'#exbox b{color:#fff}'
|
||||||
|
);
|
||||||
document.head.appendChild(s);
|
document.head.appendChild(s);
|
||||||
}
|
}
|
||||||
exbox.innerHTML = html.join('\n');
|
exbox.innerHTML = html.join('\n').replace(/https?:\/\/[^ \/]+\//g, '/').replace(/js\?_=[a-zA-Z]{4}/g, 'js');
|
||||||
exbox.style.display = 'block';
|
exbox.style.display = 'block';
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
@@ -429,6 +505,19 @@ function humansize(b, terse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function humantime(v) {
|
||||||
|
if (v >= 60 * 60 * 24)
|
||||||
|
return v;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return /.*(..:..:..).*/.exec(new Date(v * 1000).toUTCString())[1];
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function clamp(v, a, b) {
|
function clamp(v, a, b) {
|
||||||
return Math.min(Math.max(v, a), b);
|
return Math.min(Math.max(v, a), b);
|
||||||
}
|
}
|
||||||
@@ -561,6 +650,9 @@ var timer = (function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function doevents() {
|
function doevents() {
|
||||||
|
if (crashed)
|
||||||
|
return;
|
||||||
|
|
||||||
if (Date.now() - r.last < 69)
|
if (Date.now() - r.last < 69)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -606,6 +698,7 @@ var tt = (function () {
|
|||||||
r.el = this;
|
r.el = this;
|
||||||
var pos = this.getBoundingClientRect(),
|
var pos = this.getBoundingClientRect(),
|
||||||
dir = this.getAttribute('ttd') || '',
|
dir = this.getAttribute('ttd') || '',
|
||||||
|
margin = parseFloat(this.getAttribute('ttm') || 0),
|
||||||
top = pos.top < window.innerHeight / 2,
|
top = pos.top < window.innerHeight / 2,
|
||||||
big = this.className.indexOf(' ttb') !== -1;
|
big = this.className.indexOf(' ttb') !== -1;
|
||||||
|
|
||||||
@@ -617,23 +710,22 @@ var tt = (function () {
|
|||||||
r.tt.style.top = '0';
|
r.tt.style.top = '0';
|
||||||
|
|
||||||
r.tt.innerHTML = msg.replace(/\$N/g, "<br />");
|
r.tt.innerHTML = msg.replace(/\$N/g, "<br />");
|
||||||
var tw = r.tt.offsetWidth,
|
|
||||||
x = pos.left + (pos.right - pos.left) / 2 - tw / 2;
|
|
||||||
|
|
||||||
if (x < 0)
|
|
||||||
x = 8;
|
|
||||||
|
|
||||||
if (x + tw >= window.innerWidth - 8) {
|
|
||||||
x = window.innerWidth - tw - 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
r.tt.style.left = x + 'px';
|
|
||||||
r.tt.style.top = top ? pos.bottom + 'px' : 'auto';
|
|
||||||
r.tt.style.bottom = top ? 'auto' : (window.innerHeight - pos.top) + 'px';
|
|
||||||
|
|
||||||
r.el.addEventListener('mouseleave', r.hide);
|
r.el.addEventListener('mouseleave', r.hide);
|
||||||
window.addEventListener('scroll', r.hide);
|
window.addEventListener('scroll', r.hide);
|
||||||
clmod(r.tt, 'show', 1);
|
clmod(r.tt, 'show', 1);
|
||||||
|
|
||||||
|
var tw = r.tt.offsetWidth,
|
||||||
|
x = pos.left + (pos.right - pos.left) / 2 - tw / 2;
|
||||||
|
|
||||||
|
if (x + tw >= window.innerWidth - 24)
|
||||||
|
x = window.innerWidth - tw - 24;
|
||||||
|
|
||||||
|
if (x < 0)
|
||||||
|
x = 12;
|
||||||
|
|
||||||
|
r.tt.style.left = x + 'px';
|
||||||
|
r.tt.style.top = top ? (margin + pos.bottom) + 'px' : 'auto';
|
||||||
|
r.tt.style.bottom = top ? 'auto' : (margin + window.innerHeight - pos.top) + 'px';
|
||||||
};
|
};
|
||||||
|
|
||||||
r.hide = function (e) {
|
r.hide = function (e) {
|
||||||
@@ -713,6 +805,7 @@ function lf2br(txt) {
|
|||||||
var toast = (function () {
|
var toast = (function () {
|
||||||
var r = {},
|
var r = {},
|
||||||
te = null,
|
te = null,
|
||||||
|
scrolling = false,
|
||||||
obj = mknod('div');
|
obj = mknod('div');
|
||||||
|
|
||||||
obj.setAttribute('id', 'toast');
|
obj.setAttribute('id', 'toast');
|
||||||
@@ -720,38 +813,61 @@ var toast = (function () {
|
|||||||
r.visible = false;
|
r.visible = false;
|
||||||
r.txt = null;
|
r.txt = null;
|
||||||
|
|
||||||
|
function scrollchk() {
|
||||||
|
if (scrolling)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var tb = ebi('toastb'),
|
||||||
|
vis = tb.offsetHeight,
|
||||||
|
all = tb.scrollHeight;
|
||||||
|
|
||||||
|
if (8 + vis >= all)
|
||||||
|
return;
|
||||||
|
|
||||||
|
clmod(obj, 'scroll', 1);
|
||||||
|
scrolling = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unscroll() {
|
||||||
|
timer.rm(scrollchk);
|
||||||
|
clmod(obj, 'scroll');
|
||||||
|
scrolling = false;
|
||||||
|
}
|
||||||
|
|
||||||
r.hide = function (e) {
|
r.hide = function (e) {
|
||||||
ev(e);
|
ev(e);
|
||||||
|
unscroll();
|
||||||
clearTimeout(te);
|
clearTimeout(te);
|
||||||
clmod(obj, 'vis');
|
clmod(obj, 'vis');
|
||||||
r.visible = false;
|
r.visible = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
r.show = function (cl, ms, txt) {
|
r.show = function (cl, sec, txt) {
|
||||||
clearTimeout(te);
|
clearTimeout(te);
|
||||||
if (ms)
|
if (sec)
|
||||||
te = setTimeout(r.hide, ms * 1000);
|
te = setTimeout(r.hide, sec * 1000);
|
||||||
|
|
||||||
obj.innerHTML = '<a href="#" id="toastc">x</a>' + lf2br(txt);
|
obj.innerHTML = '<a href="#" id="toastc">x</a><div id="toastb">' + lf2br(txt) + '</div>';
|
||||||
obj.className = cl;
|
obj.className = cl;
|
||||||
ms += obj.offsetWidth;
|
sec += obj.offsetWidth;
|
||||||
obj.className += ' vis';
|
obj.className += ' vis';
|
||||||
ebi('toastc').onclick = r.hide;
|
ebi('toastc').onclick = r.hide;
|
||||||
|
timer.add(scrollchk);
|
||||||
r.visible = true;
|
r.visible = true;
|
||||||
r.txt = txt;
|
r.txt = txt;
|
||||||
};
|
};
|
||||||
|
|
||||||
r.ok = function (ms, txt) {
|
r.ok = function (sec, txt) {
|
||||||
r.show('ok', ms, txt);
|
r.show('ok', sec, txt);
|
||||||
};
|
};
|
||||||
r.inf = function (ms, txt) {
|
r.inf = function (sec, txt) {
|
||||||
r.show('inf', ms, txt);
|
r.show('inf', sec, txt);
|
||||||
};
|
};
|
||||||
r.warn = function (ms, txt) {
|
r.warn = function (sec, txt) {
|
||||||
r.show('warn', ms, txt);
|
r.show('warn', sec, txt);
|
||||||
};
|
};
|
||||||
r.err = function (ms, txt) {
|
r.err = function (sec, txt) {
|
||||||
r.show('err', ms, txt);
|
r.show('err', sec, txt);
|
||||||
};
|
};
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
@@ -762,8 +878,12 @@ var modal = (function () {
|
|||||||
var r = {},
|
var r = {},
|
||||||
q = [],
|
q = [],
|
||||||
o = null,
|
o = null,
|
||||||
|
cb_up = null,
|
||||||
cb_ok = null,
|
cb_ok = null,
|
||||||
cb_ng = null;
|
cb_ng = null,
|
||||||
|
prim = '<a href="#" id="modal-ok">OK</a>',
|
||||||
|
sec = '<a href="#" id="modal-ng">Cancel</a>',
|
||||||
|
ok_cancel = WINDOWS ? prim + sec : sec + prim;
|
||||||
|
|
||||||
r.busy = false;
|
r.busy = false;
|
||||||
|
|
||||||
@@ -782,9 +902,17 @@ var modal = (function () {
|
|||||||
a = ebi('modal-ok');
|
a = ebi('modal-ok');
|
||||||
a.onclick = ok;
|
a.onclick = ok;
|
||||||
|
|
||||||
(ebi('modali') || a).focus();
|
var inp = ebi('modali');
|
||||||
|
(inp || a).focus();
|
||||||
|
if (inp)
|
||||||
|
setTimeout(function () {
|
||||||
|
inp.setSelectionRange(0, inp.value.length, "forward");
|
||||||
|
}, 0);
|
||||||
|
|
||||||
document.addEventListener('focus', onfocus);
|
document.addEventListener('focus', onfocus);
|
||||||
timer.add(onfocus);
|
timer.add(onfocus);
|
||||||
|
if (cb_up)
|
||||||
|
setTimeout(cb_up, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
r.hide = function () {
|
r.hide = function () {
|
||||||
@@ -839,41 +967,44 @@ var modal = (function () {
|
|||||||
q.shift()();
|
q.shift()();
|
||||||
}
|
}
|
||||||
|
|
||||||
r.alert = function (html, cb) {
|
r.alert = function (html, cb, fun) {
|
||||||
q.push(function () {
|
q.push(function () {
|
||||||
_alert(lf2br(html), cb);
|
_alert(lf2br(html), cb, fun);
|
||||||
});
|
});
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
function _alert(html, cb) {
|
function _alert(html, cb, fun) {
|
||||||
cb_ok = cb_ng = cb;
|
cb_ok = cb_ng = cb;
|
||||||
|
cb_up = fun;
|
||||||
html += '<div id="modalb"><a href="#" id="modal-ok">OK</a></div>';
|
html += '<div id="modalb"><a href="#" id="modal-ok">OK</a></div>';
|
||||||
r.show(html);
|
r.show(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
r.confirm = function (html, cok, cng) {
|
r.confirm = function (html, cok, cng, fun) {
|
||||||
q.push(function () {
|
q.push(function () {
|
||||||
_confirm(lf2br(html), cok, cng);
|
_confirm(lf2br(html), cok, cng, fun);
|
||||||
});
|
});
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
function _confirm(html, cok, cng) {
|
function _confirm(html, cok, cng, fun) {
|
||||||
cb_ok = cok;
|
cb_ok = cok;
|
||||||
cb_ng = cng === undefined ? cok : null;
|
cb_ng = cng === undefined ? cok : null;
|
||||||
html += '<div id="modalb"><a href="#" id="modal-ok">OK</a><a href="#" id="modal-ng">Cancel</a></div>';
|
cb_up = fun;
|
||||||
|
html += '<div id="modalb">' + ok_cancel + '</div>';
|
||||||
r.show(html);
|
r.show(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
r.prompt = function (html, v, cok, cng) {
|
r.prompt = function (html, v, cok, cng, fun) {
|
||||||
q.push(function () {
|
q.push(function () {
|
||||||
_prompt(lf2br(html), v, cok, cng);
|
_prompt(lf2br(html), v, cok, cng, fun);
|
||||||
});
|
});
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
function _prompt(html, v, cok, cng) {
|
function _prompt(html, v, cok, cng, fun) {
|
||||||
cb_ok = cok;
|
cb_ok = cok;
|
||||||
cb_ng = cng === undefined ? cok : null;
|
cb_ng = cng === undefined ? cok : null;
|
||||||
html += '<input id="modali" type="text" /><div id="modalb"><a href="#" id="modal-ok">OK</a><a href="#" id="modal-ng">Cancel</a></div>';
|
cb_up = fun;
|
||||||
|
html += '<input id="modali" type="text" /><div id="modalb">' + ok_cancel + '</div>';
|
||||||
r.show(html);
|
r.show(html);
|
||||||
|
|
||||||
ebi('modali').value = v || '';
|
ebi('modali').value = v || '';
|
||||||
@@ -892,3 +1023,84 @@ function winpopup(txt) {
|
|||||||
body: 'msg=' + uricom_enc(Date.now() + ', ' + txt)
|
body: 'msg=' + uricom_enc(Date.now() + ', ' + txt)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var last_repl = null;
|
||||||
|
function repl_load() {
|
||||||
|
var ipre = ebi('repl_pre'),
|
||||||
|
tb = ebi('modali');
|
||||||
|
|
||||||
|
function getpres() {
|
||||||
|
var o, ret = jread("repl_pre", []);
|
||||||
|
if (!ret.length)
|
||||||
|
ret = [
|
||||||
|
'var v=Object.keys(localStorage); v.sort(); JSON.stringify(v)',
|
||||||
|
'console.hist.slice(-10).join("\\n")'
|
||||||
|
];
|
||||||
|
|
||||||
|
ipre.innerHTML = '<option value=""></option>';
|
||||||
|
for (var a = 0; a < ret.length; a++) {
|
||||||
|
o = mknod('option');
|
||||||
|
o.setAttribute('value', ret[a]);
|
||||||
|
o.textContent = ret[a];
|
||||||
|
ipre.appendChild(o);
|
||||||
|
}
|
||||||
|
last_repl = ipre.value = (last_repl || (ret.length ? ret.slice(-1)[0] : ''));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ebi('repl_pdel').onclick = function (e) {
|
||||||
|
var val = ipre.value,
|
||||||
|
pres = getpres();
|
||||||
|
|
||||||
|
apop(pres, val);
|
||||||
|
jwrite('repl_pre', pres);
|
||||||
|
getpres();
|
||||||
|
};
|
||||||
|
ebi('repl_pnew').onclick = function (e) {
|
||||||
|
var val = tb.value,
|
||||||
|
pres = getpres();
|
||||||
|
|
||||||
|
apop(pres, ipre.value);
|
||||||
|
pres.push(val);
|
||||||
|
jwrite('repl_pre', pres);
|
||||||
|
getpres();
|
||||||
|
ipre.value = val;
|
||||||
|
};
|
||||||
|
ipre.oninput = ipre.onchange = function () {
|
||||||
|
tb.value = last_repl = ipre.value;
|
||||||
|
};
|
||||||
|
tb.oninput = function () {
|
||||||
|
last_repl = this.value;
|
||||||
|
};
|
||||||
|
getpres();
|
||||||
|
tb.value = last_repl;
|
||||||
|
setTimeout(function () {
|
||||||
|
tb.setSelectionRange(0, tb.value.length, "forward");
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
function repl(e) {
|
||||||
|
ev(e);
|
||||||
|
var html = [
|
||||||
|
'<p>js repl (prefix with <code>,</code> to allow raise)</p>',
|
||||||
|
'<p><select id="repl_pre"></select>',
|
||||||
|
' <button id="repl_pdel">❌ del</button>',
|
||||||
|
' <button id="repl_pnew">💾 SAVE</button></p>'
|
||||||
|
];
|
||||||
|
|
||||||
|
modal.prompt(html.join(''), '', function (cmd) {
|
||||||
|
if (!cmd)
|
||||||
|
return toast.inf(3, 'eval aborted');
|
||||||
|
|
||||||
|
if (cmd.startsWith(','))
|
||||||
|
return modal.alert(esc(eval(cmd.slice(1)) + ''))
|
||||||
|
|
||||||
|
try {
|
||||||
|
modal.alert(esc(eval(cmd) + ''));
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
modal.alert('<h6>exception</h6>' + esc(ex + ''));
|
||||||
|
}
|
||||||
|
}, undefined, repl_load);
|
||||||
|
}
|
||||||
|
if (ebi('repl'))
|
||||||
|
ebi('repl').onclick = repl;
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
/* video */
|
/* video, alternative 1:
|
||||||
|
top-left icon, just like the other formats
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
#ggrid>a:is(
|
#ggrid>a:is(
|
||||||
[href$=".mkv"i],
|
[href$=".mkv"i],
|
||||||
[href$=".mp4"i],
|
[href$=".mp4"i],
|
||||||
@@ -6,6 +9,40 @@
|
|||||||
):before {
|
):before {
|
||||||
content: '📺';
|
content: '📺';
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* video, alternative 2:
|
||||||
|
play-icon in the middle of the thumbnail
|
||||||
|
=======================================================================
|
||||||
|
*/
|
||||||
|
#ggrid>a:is(
|
||||||
|
[href$=".mkv"i],
|
||||||
|
[href$=".mp4"i],
|
||||||
|
[href$=".webm"i],
|
||||||
|
) {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
#ggrid>a:is(
|
||||||
|
[href$=".mkv"i],
|
||||||
|
[href$=".mp4"i],
|
||||||
|
[href$=".webm"i],
|
||||||
|
):before {
|
||||||
|
content: '▶';
|
||||||
|
opacity: .8;
|
||||||
|
margin: 0;
|
||||||
|
padding: 1em .5em 1em .7em;
|
||||||
|
border-radius: 9em;
|
||||||
|
line-height: 0;
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: none;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
left: calc(50% - 1em);
|
||||||
|
top: calc(50% - 1.4em);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* audio */
|
/* audio */
|
||||||
@@ -21,6 +58,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* image */
|
/* image */
|
||||||
#ggrid>a:is(
|
#ggrid>a:is(
|
||||||
[href$=".jpg"i],
|
[href$=".jpg"i],
|
||||||
|
|||||||
@@ -27,6 +27,9 @@
|
|||||||
/* adjust the button area a bit */
|
/* adjust the button area a bit */
|
||||||
#u2conf.has_btn {width: 35em !important; margin: 5em auto}
|
#u2conf.has_btn {width: 35em !important; margin: 5em auto}
|
||||||
|
|
||||||
|
/* a */
|
||||||
|
#op_up2k {min-height: 0}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<a href="#" onclick="this.parentNode.innerHTML='';">show advanced options</a>
|
<a href="#" onclick="this.parentNode.innerHTML='';">show advanced options</a>
|
||||||
|
|||||||
@@ -130,6 +130,9 @@ sqlite3 .hist/up2k.db 'select * from mt where k="fgsfds" or k="t:mtp"' | tee /de
|
|||||||
for ((f=420;f<1200;f++)); do sz=$(ffmpeg -y -f lavfi -i sine=frequency=$f:duration=2 -vf volume=0.1 -ac 1 -ar 44100 -f s16le /dev/shm/a.wav 2>/dev/null; base64 -w0 </dev/shm/a.wav | gzip -c | wc -c); printf '%d %d\n' $f $sz; done | tee /dev/stderr | sort -nrk2,2
|
for ((f=420;f<1200;f++)); do sz=$(ffmpeg -y -f lavfi -i sine=frequency=$f:duration=2 -vf volume=0.1 -ac 1 -ar 44100 -f s16le /dev/shm/a.wav 2>/dev/null; base64 -w0 </dev/shm/a.wav | gzip -c | wc -c); printf '%d %d\n' $f $sz; done | tee /dev/stderr | sort -nrk2,2
|
||||||
ffmpeg -y -f lavfi -i sine=frequency=1050:duration=2 -vf volume=0.1 -ac 1 -ar 44100 /dev/shm/a.wav
|
ffmpeg -y -f lavfi -i sine=frequency=1050:duration=2 -vf volume=0.1 -ac 1 -ar 44100 /dev/shm/a.wav
|
||||||
|
|
||||||
|
# play icon calibration pics
|
||||||
|
for w in 150 170 190 210 230 250; do for h in 130 150 170 190 210; do /c/Program\ Files/ImageMagick-7.0.11-Q16-HDRI/magick.exe convert -size ${w}x${h} xc:brown -fill orange -draw "circle $((w/2)),$((h/2)) $((w/2)),$((h/3))" $w-$h.png; done; done
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
## vscode
|
## vscode
|
||||||
@@ -161,7 +164,7 @@ brew install python@2
|
|||||||
pip install virtualenv
|
pip install virtualenv
|
||||||
|
|
||||||
# readme toc
|
# readme toc
|
||||||
cat README.md | awk 'function pr() { if (!h) {return}; if (/^ *[*!#]/||!s) {printf "%s\n",h;h=0;return}; if (/.../) {printf "%s - %s\n",h,$0;h=0}; }; /^#/{s=1;pr()} /^#* *(file indexing|install on android|dev env setup|just the sfx|complete release|optional gpl stuff)|`$/{s=0} /^#/{lv=length($1);sub(/[^ ]+ /,"");bab=$0;gsub(/ /,"-",bab); h=sprintf("%" ((lv-1)*4+1) "s [%s](#%s)", "*",$0,bab);next} !h{next} {sub(/:$/,"")} {pr()}' > toc; grep -E '^## readme toc' -B1000 -A2 <README.md >p1; grep -E '^## quickstart' -B2 -A999999 <README.md >p2; (cat p1; grep quickstart -A1000 <toc; cat p2) >README.md
|
cat README.md | awk 'function pr() { if (!h) {return}; if (/^ *[*!#]/||!s) {printf "%s\n",h;h=0;return}; if (/.../) {printf "%s - %s\n",h,$0;h=0}; }; /^#/{s=1;pr()} /^#* *(file indexing|install on android|dev env setup|just the sfx|complete release|optional gpl stuff)|`$/{s=0} /^#/{lv=length($1);sub(/[^ ]+ /,"");bab=$0;gsub(/ /,"-",bab); h=sprintf("%" ((lv-1)*4+1) "s [%s](#%s)", "*",$0,bab);next} !h{next} {sub(/ .*/,"");sub(/[:,]$/,"")} {pr()}' > toc; grep -E '^## readme toc' -B1000 -A2 <README.md >p1; grep -E '^## quickstart' -B2 -A999999 <README.md >p2; (cat p1; grep quickstart -A1000 <toc; cat p2) >README.md
|
||||||
|
|
||||||
# fix firefox phantom breakpoints,
|
# fix firefox phantom breakpoints,
|
||||||
# suggestions from bugtracker, doesnt work (debugger is not attachable)
|
# suggestions from bugtracker, doesnt work (debugger is not attachable)
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ help() { exec cat <<'EOF'
|
|||||||
#
|
#
|
||||||
# `no-sh` makes just the python sfx, skips the sh/unix sfx
|
# `no-sh` makes just the python sfx, skips the sh/unix sfx
|
||||||
#
|
#
|
||||||
# `no-ogv` saves ~500k by removing the opus/vorbis audio codecs
|
# `no-ogv` saves ~192k by removing the opus/vorbis audio codecs
|
||||||
# (only affects apple devices; everything else has native support)
|
# (only affects apple devices; everything else has native support)
|
||||||
#
|
#
|
||||||
# `no-cm` saves ~90k by removing easymde/codemirror
|
# `no-cm` saves ~92k by removing easymde/codemirror
|
||||||
# (the fancy markdown editor)
|
# (the fancy markdown editor)
|
||||||
#
|
#
|
||||||
# `no-fnt` saves ~9k by removing the source-code-pro font
|
# `no-fnt` saves ~9k by removing the source-code-pro font
|
||||||
# (mainly used my the markdown viewer/editor)
|
# (browsers will try to use 'Consolas' instead)
|
||||||
#
|
#
|
||||||
# `no-dd` saves ~2k by removing the mouse cursor
|
# `no-dd` saves ~2k by removing the mouse cursor
|
||||||
|
|
||||||
@@ -37,6 +37,8 @@ gtar=$(command -v gtar || command -v gnutar) || true
|
|||||||
sed() { gsed "$@"; }
|
sed() { gsed "$@"; }
|
||||||
find() { gfind "$@"; }
|
find() { gfind "$@"; }
|
||||||
sort() { gsort "$@"; }
|
sort() { gsort "$@"; }
|
||||||
|
shuf() { gshuf "$@"; }
|
||||||
|
nproc() { gnproc; }
|
||||||
sha1sum() { shasum "$@"; }
|
sha1sum() { shasum "$@"; }
|
||||||
unexpand() { gunexpand "$@"; }
|
unexpand() { gunexpand "$@"; }
|
||||||
command -v grealpath >/dev/null &&
|
command -v grealpath >/dev/null &&
|
||||||
@@ -144,6 +146,7 @@ tmpdir="$(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ver=
|
ver=
|
||||||
|
[ -z "$repack" ] &&
|
||||||
git describe --tags >/dev/null 2>/dev/null && {
|
git describe --tags >/dev/null 2>/dev/null && {
|
||||||
git_ver="$(git describe --tags)"; # v0.5.5-2-gb164aa0
|
git_ver="$(git describe --tags)"; # v0.5.5-2-gb164aa0
|
||||||
ver="$(printf '%s\n' "$git_ver" | sed -r 's/^v//')";
|
ver="$(printf '%s\n' "$git_ver" | sed -r 's/^v//')";
|
||||||
@@ -175,7 +178,7 @@ git describe --tags >/dev/null 2>/dev/null && {
|
|||||||
|
|
||||||
[ -z "$ver" ] &&
|
[ -z "$ver" ] &&
|
||||||
ver="$(awk '/^VERSION *= \(/ {
|
ver="$(awk '/^VERSION *= \(/ {
|
||||||
gsub(/[^0-9,]/,""); gsub(/,/,"."); print; exit}' < copyparty/__version__.py)"
|
gsub(/[^0-9,a-g-]/,""); gsub(/,/,"."); print; exit}' < copyparty/__version__.py)"
|
||||||
|
|
||||||
ts=$(date -u +%s)
|
ts=$(date -u +%s)
|
||||||
hts=$(date -u +%Y-%m%d-%H%M%S) # --date=@$ts (thx osx)
|
hts=$(date -u +%Y-%m%d-%H%M%S) # --date=@$ts (thx osx)
|
||||||
@@ -214,17 +217,17 @@ done
|
|||||||
|
|
||||||
[ $no_fnt ] && {
|
[ $no_fnt ] && {
|
||||||
rm -f copyparty/web/deps/scp.woff2
|
rm -f copyparty/web/deps/scp.woff2
|
||||||
f=copyparty/web/md.css
|
f=copyparty/web/ui.css
|
||||||
gzip -d "$f"
|
gzip -d "$f.gz" || true
|
||||||
sed -r '/scp\.woff2/d' <$f >t
|
sed -r "s/src:.*scp.*\)/src:local('Consolas')/" <$f >t
|
||||||
tmv "$f"
|
tmv "$f"
|
||||||
}
|
}
|
||||||
|
|
||||||
[ $no_dd ] && {
|
[ $no_dd ] && {
|
||||||
rm -rf copyparty/web/dd
|
rm -rf copyparty/web/dd
|
||||||
f=copyparty/web/browser.css
|
f=copyparty/web/browser.css
|
||||||
gzip -d "$f"
|
gzip -d "$f.gz" || true
|
||||||
sed -r 's/(cursor: )url\([^)]+\), (pointer)/\1\2/; /[0-9]+% \{cursor:/d; /animation: cursor/d' <$f >t
|
sed -r 's/(cursor: ?)url\([^)]+\), ?(pointer)/\1\2/; /[0-9]+% \{cursor:/d; /animation: ?cursor/d' <$f >t
|
||||||
tmv "$f"
|
tmv "$f"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,6 +241,12 @@ f=dep-j2/jinja2/constants.py
|
|||||||
awk '/^LOREM_IPSUM_WORDS/{o=1;print "LOREM_IPSUM_WORDS = u\"a\"";next} !o; /"""/{o=0}' <$f >t
|
awk '/^LOREM_IPSUM_WORDS/{o=1;print "LOREM_IPSUM_WORDS = u\"a\"";next} !o; /"""/{o=0}' <$f >t
|
||||||
tmv "$f"
|
tmv "$f"
|
||||||
|
|
||||||
|
grep -rLE '^#[^a-z]*coding: utf-8' dep-j2 |
|
||||||
|
while IFS= read -r f; do
|
||||||
|
(echo "# coding: utf-8"; cat "$f") >t
|
||||||
|
tmv "$f"
|
||||||
|
done
|
||||||
|
|
||||||
# up2k goes from 28k to 22k laff
|
# up2k goes from 28k to 22k laff
|
||||||
awk 'BEGIN{gensub(//,"",1)}' </dev/null &&
|
awk 'BEGIN{gensub(//,"",1)}' </dev/null &&
|
||||||
echo entabbening &&
|
echo entabbening &&
|
||||||
@@ -262,14 +271,27 @@ done
|
|||||||
|
|
||||||
gzres() {
|
gzres() {
|
||||||
command -v pigz &&
|
command -v pigz &&
|
||||||
pk='pigz -11 -I 256' ||
|
pk='pigz -11 -I 2560' ||
|
||||||
pk='gzip'
|
pk='gzip'
|
||||||
|
|
||||||
echo "$pk"
|
np=$(nproc)
|
||||||
find | grep -E '\.(js|css)$' | grep -vF /deps/ | while IFS= read -r f; do
|
echo "$pk #$np"
|
||||||
echo -n .
|
|
||||||
$pk "$f"
|
while IFS=' ' read -r _ f; do
|
||||||
|
while true; do
|
||||||
|
na=$(ps auxwww | grep -F "$pk" | wc -l)
|
||||||
|
[ $na -le $np ] && break
|
||||||
|
sleep 0.2
|
||||||
done
|
done
|
||||||
|
echo -n .
|
||||||
|
$pk "$f" &
|
||||||
|
done < <(
|
||||||
|
find -printf '%s %p\n' |
|
||||||
|
grep -E '\.(js|css)$' |
|
||||||
|
grep -vF /deps/ |
|
||||||
|
sort -nr
|
||||||
|
)
|
||||||
|
wait
|
||||||
echo
|
echo
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +325,7 @@ for d in copyparty dep-j2; do find $d -type f; done |
|
|||||||
sed -r 's/(.*)\.(.*)/\2 \1/' | LC_ALL=C sort |
|
sed -r 's/(.*)\.(.*)/\2 \1/' | LC_ALL=C sort |
|
||||||
sed -r 's/([^ ]*) (.*)/\2.\1/' | grep -vE '/list1?$' > list1
|
sed -r 's/([^ ]*) (.*)/\2.\1/' | grep -vE '/list1?$' > list1
|
||||||
|
|
||||||
(grep -vE '\.(gz|br)$' list1; grep -E '\.(gz|br)$' list1) >list || true
|
(grep -vE '\.(gz|br)$' list1; grep -E '\.(gz|br)$' list1 | shuf) >list || true
|
||||||
|
|
||||||
echo creating tar
|
echo creating tar
|
||||||
args=(--owner=1000 --group=1000)
|
args=(--owner=1000 --group=1000)
|
||||||
|
|||||||
Reference in New Issue
Block a user