Rename the file server from the old gossa tree. Binary, Go sources, headers, and docs are wormhole throughout.
Unified · Split
| @@ -0,0 +1 @@ | ||
| 1 | +ui/** linguist-vendored | |
| @@ -0,0 +1,20 @@ | ||
| 1 | +name: always | |
| 2 | +on: [push] | |
| 3 | +jobs: | |
| 4 | + build: | |
| 5 | + name: Build | |
| 6 | + runs-on: ubuntu-latest | |
| 7 | + steps: | |
| 8 | + | |
| 9 | + - name: Checkout code | |
| 10 | + uses: actions/checkout@v4 | |
| 11 | + | |
| 12 | + - name: Set up Go | |
| 13 | + uses: actions/setup-go@v5 | |
| 14 | + with: | |
| 15 | + go-version: 1.23.0 | |
| 16 | + id: go | |
| 17 | + | |
| 18 | + - name: Run | |
| 19 | + run: make ci | |
| 20 | + | |
| @@ -0,0 +1,57 @@ | ||
| 1 | +name: deploy | |
| 2 | + | |
| 3 | +on: | |
| 4 | + push: | |
| 5 | + tags: | |
| 6 | + - '*' | |
| 7 | + | |
| 8 | +jobs: | |
| 9 | + build: | |
| 10 | + runs-on: ubuntu-latest | |
| 11 | + steps: | |
| 12 | + - name: Checkout | |
| 13 | + uses: actions/checkout@v4 | |
| 14 | + | |
| 15 | + - name: Set env | |
| 16 | + run: echo "GIT_TAG=`echo $(git describe --tags --abbrev=0)`" >> $GITHUB_ENV | |
| 17 | + | |
| 18 | + - name: Set up QEMU | |
| 19 | + uses: docker/setup-qemu-action@v3 | |
| 20 | + | |
| 21 | + - name: Login to DockerHub | |
| 22 | + uses: docker/login-action@v3 | |
| 23 | + with: | |
| 24 | + username: ${{ secrets.DOCKER_USER }} | |
| 25 | + password: ${{ secrets.DOCKER_PASSWORD }} | |
| 26 | + | |
| 27 | + - name: Set up Docker Buildx | |
| 28 | + uses: docker/setup-buildx-action@v3 | |
| 29 | + | |
| 30 | + - name: Build and release on dockerhub | |
| 31 | + uses: docker/build-push-action@v6 | |
| 32 | + with: | |
| 33 | + file: support/build.Dockerfile | |
| 34 | + push: true | |
| 35 | + platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64 | |
| 36 | + tags: krisyotam/wormhole:latest,krisyotam/wormhole:${{github.ref_name}} | |
| 37 | + | |
| 38 | + - name: Build all artifacts | |
| 39 | + run: make build-all | |
| 40 | + | |
| 41 | + - name: "Release gh release versioned" | |
| 42 | + uses: ncipollo/release-action@v1 | |
| 43 | + with: | |
| 44 | + allowUpdates: true | |
| 45 | + artifacts: "builds/*" | |
| 46 | + bodyFile: "builds/buildout" | |
| 47 | + token: ${{ secrets.GITHUB_TOKEN }} | |
| 48 | + | |
| 49 | + - name: "Release gh release latest" | |
| 50 | + uses: ncipollo/release-action@v1 | |
| 51 | + with: | |
| 52 | + tag: latest | |
| 53 | + name: Latest | |
| 54 | + allowUpdates: true | |
| 55 | + artifacts: "builds/*" | |
| 56 | + bodyFile: "builds/buildout" | |
| 57 | + token: ${{ secrets.GITHUB_TOKEN }} | |
| @@ -0,0 +1,48 @@ | ||
| 1 | +wormhole | |
| 2 | +wormhole-linux64 | |
| 3 | +wormhole-linux-arm | |
| 4 | +wormhole-linux-arm64 | |
| 5 | +wormhole-mac | |
| 6 | +wormhole-windows.exe | |
| 7 | +builds | |
| 8 | +wormhole.test | |
| 9 | + | |
| 10 | +*.out | |
| 11 | +.vscode | |
| 12 | +test-fixture/* | |
| 13 | +test-fixture/*/* | |
| 14 | +!test-fixture/.testhidden | |
| 15 | +!test-fixture/compress | |
| 16 | +!test-fixture/compress/foo* | |
| 17 | +!test-fixture/gzip | |
| 18 | +!test-fixture/gzip/fake_ecstatic* | |
| 19 | +!test-fixture/gzip/real_ecstatic | |
| 20 | +!test-fixture/curimit@gmail.com (40%) | |
| 21 | +!test-fixture/curimit@gmail.com (40%)/test.html | |
| 22 | +!test-fixture/show-dir$$href_encoding$$ | |
| 23 | +!test-fixture/show-dir$$href_encoding$$/aname+aplus.txt | |
| 24 | +!test-fixture/subdir | |
| 25 | +!test-fixture/subdir/e.html | |
| 26 | +!test-fixture/subdir_with space | |
| 27 | +!test-fixture/subdir_with space/file_with space.html | |
| 28 | +!test-fixture/中文 | |
| 29 | +!test-fixture/中文/檔案.html | |
| 30 | +!test-fixture/404* | |
| 31 | +!test-fixture/b.txt | |
| 32 | +!test-fixture/custom_mime_type* | |
| 33 | +!test-fixture/f_f | |
| 34 | +!test-fixture/hols | |
| 35 | +!test-fixture/hols/c.js | |
| 36 | +!test-fixture/hols/adler-2872995_1920.jpg | |
| 37 | +!test-fixture/hols/elephant-1822636_1920.jpg | |
| 38 | +!test-fixture/hols/glasgow.jpg | |
| 39 | +!test-fixture/hols/landscape-540116_1920.jpg | |
| 40 | +!test-fixture/hols/scotland-1761292_1920.jpg | |
| 41 | +!test-fixture/fancy-path | |
| 42 | +!test-fixture/fancy-path/* | |
| 43 | +!test-fixture/ext/* | |
| 44 | +!test-fixture/ext | |
| 45 | +!test-fixture/.some-hidden-file | |
| 46 | +!test-fixture/hols/.hidden-folder | |
| 47 | +!test-fixture/hols/.hidden-folder/some-file | |
| 48 | +!test-fixture/compress/hols-sym | |
| @@ -0,0 +1,10 @@ | ||
| 1 | +FROM golang:1.23-alpine AS builder | |
| 2 | +WORKDIR /src | |
| 3 | +COPY go.mod ./ | |
| 4 | +RUN go mod download | |
| 5 | +COPY . . | |
| 6 | +RUN CGO_ENABLED=0 go build -trimpath -o /wormhole | |
| 7 | + | |
| 8 | +FROM alpine:3.20 | |
| 9 | +COPY --from=builder /wormhole /usr/local/bin/wormhole | |
| 10 | +ENTRYPOINT ["wormhole"] | |
| @@ -0,0 +1,21 @@ | ||
| 1 | +MIT License | |
| 2 | + | |
| 3 | +Copyright (c) 2018 Pierre Dubouilh | |
| 4 | + | |
| 5 | +Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | +of this software and associated documentation files (the "Software"), to deal | |
| 7 | +in the Software without restriction, including without limitation the rights | |
| 8 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | +copies of the Software, and to permit persons to whom the Software is | |
| 10 | +furnished to do so, subject to the following conditions: | |
| 11 | + | |
| 12 | +The above copyright notice and this permission notice shall be included in all | |
| 13 | +copies or substantial portions of the Software. | |
| 14 | + | |
| 15 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| 21 | +SOFTWARE. | |
| @@ -0,0 +1,75 @@ | ||
| 1 | +FLAGS := -trimpath | |
| 2 | +NOCGO := CGO_ENABLED=0 | |
| 3 | + | |
| 4 | +build:: | |
| 5 | + go vet && go fmt | |
| 6 | + ${NOCGO} go build ${FLAGS} -o wormhole | |
| 7 | + | |
| 8 | +install:: | |
| 9 | + sudo cp wormhole /usr/local/bin | |
| 10 | + | |
| 11 | +run:: | |
| 12 | + ./wormhole -verb=true test-fixture | |
| 13 | + | |
| 14 | +run-ro:: | |
| 15 | + ./wormhole -verb=true -ro=true test-fixture | |
| 16 | + | |
| 17 | +run-extra:: | |
| 18 | + ./wormhole -verb=true -prefix="/fancy-path/" -k=false -symlinks=true test-fixture | |
| 19 | + | |
| 20 | +ci:: build-all test | |
| 21 | + echo "done" | |
| 22 | + | |
| 23 | +test:: | |
| 24 | + -@cd test-fixture && ln -s ../support .; true | |
| 25 | + go test -cover -c -tags testrunmain | |
| 26 | + | |
| 27 | + timeout -s SIGINT 3 ./wormhole.test -test.coverprofile=normal.out -test.run '^TestRunMain' -verb=true test-fixture & | |
| 28 | + sleep 2 | |
| 29 | + go test -run TestNormal | |
| 30 | + sleep 1 | |
| 31 | + | |
| 32 | + timeout -s SIGINT 3 ./wormhole.test -test.coverprofile=extra.out -test.run '^TestRunMain' -prefix='/fancy-path/' -k=false -symlinks=true test-fixture & | |
| 33 | + sleep 2 | |
| 34 | + go test -run TestExtra | |
| 35 | + sleep 1 | |
| 36 | + | |
| 37 | + timeout -s SIGINT 3 ./wormhole.test -test.coverprofile=ro.out -test.run '^TestRunMain' -ro=true test-fixture & | |
| 38 | + sleep 2 | |
| 39 | + go test -run TestRo | |
| 40 | + sleep 1 | |
| 41 | + | |
| 42 | + # gocovmerge ro.out extra.out normal.out > all.out | |
| 43 | + # go tool cover -html all.out | |
| 44 | + # go tool cover -func=all.out | grep main | grep '9.\..\%' | |
| 45 | + | |
| 46 | +watch:: | |
| 47 | + ls wormhole.go wormhole_test.go ui/* | entr -rc make build run | |
| 48 | + | |
| 49 | +watch-extra:: | |
| 50 | + ls wormhole.go wormhole_test.go ui/* | entr -rc make build run-extra | |
| 51 | + | |
| 52 | +watch-ro:: | |
| 53 | + ls wormhole.go wormhole_test.go ui/* | entr -rc make build run-ro | |
| 54 | + | |
| 55 | +watch-test:: | |
| 56 | + ls wormhole.go wormhole_test.go ui/* | entr -rc make test | |
| 57 | + | |
| 58 | +build-all:: build | |
| 59 | + go version | |
| 60 | + ${NOCGO} GOOS=linux GOARCH=amd64 go build ${FLAGS} -o builds/wormhole-linux-x64 | |
| 61 | + ${NOCGO} GOOS=linux GOARCH=arm go build ${FLAGS} -o builds/wormhole-linux-arm | |
| 62 | + ${NOCGO} GOOS=linux GOARCH=arm64 go build ${FLAGS} -o builds/wormhole-linux-arm64 | |
| 63 | + ${NOCGO} GOOS=darwin GOARCH=amd64 go build ${FLAGS} -o builds/wormhole-mac-x64 | |
| 64 | + ${NOCGO} GOOS=darwin GOARCH=arm64 go build ${FLAGS} -o builds/wormhole-mac-arm64 | |
| 65 | + ${NOCGO} GOOS=windows GOARCH=amd64 go build ${FLAGS} -o builds/wormhole-windows.exe | |
| 66 | + sha256sum builds/* | tee builds/buildout | |
| 67 | + | |
| 68 | +clean:: | |
| 69 | + rm -f wormhole | |
| 70 | + rm -f wormhole-linux64 | |
| 71 | + rm -f wormhole-linux-arm | |
| 72 | + rm -f wormhole-linux-arm64 | |
| 73 | + rm -f wormhole-mac | |
| 74 | + rm -f wormhole-windows.exe | |
| 75 | + | |
| @@ -0,0 +1,35 @@ | ||
| 1 | +# wormhole | |
| 2 | + | |
| 3 | +A small Go file server with a built-in browser. One binary. No extra runtime. | |
| 4 | + | |
| 5 | +You point it at a directory and it serves it. Browse files, stream video, look at pictures and PDFs, take notes, upload if you want. Keyboard navigation is there. Read-only mode is there if you only want people to look. | |
| 6 | + | |
| 7 | +HTTPS and logins stay with a reverse proxy. wormhole does the files. | |
| 8 | + | |
| 9 | +## Build | |
| 10 | + | |
| 11 | +```sh | |
| 12 | +make | |
| 13 | +./wormhole --help | |
| 14 | +./wormhole -h 127.0.0.1 ~/storage | |
| 15 | +``` | |
| 16 | + | |
| 17 | +`make install` copies the binary to `/usr/local/bin`. | |
| 18 | + | |
| 19 | +## Docker | |
| 20 | + | |
| 21 | +```sh | |
| 22 | +mkdir ~/LocalDirToShare | |
| 23 | +docker build -t wormhole . | |
| 24 | +docker run -v ~/LocalDirToShare:/shared -p 8001:8001 wormhole | |
| 25 | +``` | |
| 26 | + | |
| 27 | +See `support/` for compose, a systemd unit, and Caddy examples for more than one user. | |
| 28 | + | |
| 29 | +## Shortcuts | |
| 30 | + | |
| 31 | +`Ctrl/Cmd + h` lists the UI keys. | |
| 32 | + | |
| 33 | +## License | |
| 34 | + | |
| 35 | +MIT. | |
| @@ -0,0 +1,3 @@ | ||
| 1 | +module github.com/krisyotam/wormhole | |
| 2 | + | |
| 3 | +go 1.23.0 | |
| @@ -0,0 +1,16 @@ | ||
| 1 | +FROM docker.io/library/golang:1.23.0-alpine AS builder | |
| 2 | +RUN apk add --no-cache make | |
| 3 | +WORKDIR /wormholeSrc | |
| 4 | +COPY . /wormholeSrc | |
| 5 | +RUN make | |
| 6 | + | |
| 7 | +FROM docker.io/library/alpine:3.20 | |
| 8 | +ENV UID="1000" GID="1000" HOST="0.0.0.0" PORT="8001" PREFIX="/" FOLLOW_SYMLINKS="false" SKIP_HIDDEN_FILES="true" DATADIR="/shared" READONLY="false" VERB="false" | |
| 9 | +COPY --from=builder /wormholeSrc/wormhole /wormhole | |
| 10 | +RUN addgroup -g ${GID} user \ | |
| 11 | + && adduser -D -u ${UID} -G user user | |
| 12 | +WORKDIR ${DATADIR} | |
| 13 | +RUN chown ${UID}:${GID} ${DATADIR} | |
| 14 | +USER ${UID}:${GID} | |
| 15 | +ENTRYPOINT /wormhole -h ${HOST} -p ${PORT} -k=${SKIP_HIDDEN_FILES} -ro=${READONLY} --symlinks=${FOLLOW_SYMLINKS} --prefix=${PREFIX} --verb=${VERB} ${DATADIR} | |
| 16 | +HEALTHCHECK --timeout=5s --start-period=5s --retries=3 CMD wget --no-verbose --tries=1 --spider 127.0.0.1:8001 || exit 1 | |
| @@ -0,0 +1,39 @@ | ||
| 1 | +version: '2' | |
| 2 | + | |
| 3 | +services: | |
| 4 | + wormhole-server: | |
| 5 | + image: docker.io/krisyotam/wormhole:latest | |
| 6 | + container_name: wormhole | |
| 7 | + restart: always | |
| 8 | + read_only: true | |
| 9 | + # uncomment to set the user | |
| 10 | + # user: "1000:1000" | |
| 11 | + # userns_mode: "keep-id" # uncomment if using rootless podman as well as the x-podman directive at the bottom | |
| 12 | + # environment: | |
| 13 | + #- READONLY=true # uncomment to set wormhole as read only | |
| 14 | + # - UID=1000 # this should match the user set above | |
| 15 | + # - GID=1000 # this should match the user's group | |
| 16 | + cap_drop: | |
| 17 | + - ALL | |
| 18 | + cap_add: | |
| 19 | + - SETGID | |
| 20 | + - SETUID | |
| 21 | + # uncomment to set resource usage limits | |
| 22 | + # deploy: | |
| 23 | + # resources: | |
| 24 | + # limits: | |
| 25 | + # cpus: "2" | |
| 26 | + # memory: 250m | |
| 27 | + # pids: 1024 | |
| 28 | + ports: | |
| 29 | + - 8001:8001 | |
| 30 | + volumes: | |
| 31 | + - ~/to-share:/shared | |
| 32 | +# labels: | |
| 33 | +# - "traefik.enable=true" | |
| 34 | +# - "traefik.port=8001" | |
| 35 | +# - "traefik.backend=wormhole" | |
| 36 | +# - "traefik.frontend.rule=Host:${WORMHOLE}.${DOMAIN}" | |
| 37 | + | |
| 38 | +# x-podman: # uncomment if using rootless podman as well as the userns_mode directive at the top | |
| 39 | +# in_pod: false | |
| @@ -0,0 +1,195 @@ | ||
| 1 | +## run as a service | |
| 2 | + | |
| 3 | +use the `service` file provided here to use as a systemd service. | |
| 4 | + | |
| 5 | +```sh | |
| 6 | +# edit service file | |
| 7 | + | |
| 8 | +# copy over our service, and start. Notice no sudo with systemctl command ! | |
| 9 | +% mkdir -p ~/.config/systemd/user/ | |
| 10 | +% cp service ~/.config/systemd/user/wormhole.service | |
| 11 | +% systemctl --user start wormhole | |
| 12 | + | |
| 13 | +# enable at boot time | |
| 14 | +% systemctl --user enable wormhole | |
| 15 | +``` | |
| 16 | + | |
| 17 | +## run with docker | |
| 18 | + | |
| 19 | +the master branch is automatically built and pushed to [dockerhub](https://hub.docker.com/r/krisyotam/wormhole) under `krisyotam/wormhole`. | |
| 20 | + | |
| 21 | +```sh | |
| 22 | +# pull from dockerhub and run | |
| 23 | +% mkdir ~/LocalDirToShare | |
| 24 | +% sudo docker run -v ~/LocalDirToShare:/shared -p 8001:8001 krisyotam/wormhole | |
| 25 | + | |
| 26 | +# options are settable through env. variabes. all the options are the build.Dockerfile | |
| 27 | +% sudo docker run -e PREFIX="/wormhole/" -v ~/LocalDirToShare:/shared -p 8001:8001 krisyotam/wormhole | |
| 28 | +``` | |
| 29 | + | |
| 30 | +if you prefer building the image yourself : | |
| 31 | + | |
| 32 | +```sh | |
| 33 | +# build wormhole within a build container, needs to be ran within the sources, ../ from here, and run | |
| 34 | +% mkdir ~/LocalDirToShare | |
| 35 | +% docker build -t wormhole -f support/build.Dockerfile . | |
| 36 | +% sudo docker run -v ~/LocalDirToShare:/shared -p 8001:8001 wormhole | |
| 37 | +``` | |
| 38 | + | |
| 39 | +a docker-compose example image is also provided. running docker compose should be straightforward : `docker-compose up .` have a look in `docker-compose.yml` for further configuration. | |
| 40 | + | |
| 41 | +## multi-account setup with Caddy | |
| 42 | + | |
| 43 | +authentication / user routing has been left out of the design of wormhole, as simple tools are already available for this purpose. [caddy](https://caddyserver.com) is used here as an example, but other proxy can be used in a similar fashion. | |
| 44 | + | |
| 45 | +### example 1 root, multiple read-only users | |
| 46 | + | |
| 47 | +This sample Caddyfile will | |
| 48 | + + enable https on the domain myserver.com (http will be automatically redirected to https) | |
| 49 | + + password protect the access | |
| 50 | + + route the root user requests to 1 wormhole instance | |
| 51 | + + route user1 and user2 requests to a readonly wormhole instance | |
| 52 | + | |
| 53 | +<details> | |
| 54 | + <summary>Legacy Caddy v1 Caddyfile</summary> | |
| 55 | + | |
| 56 | + ```sh | |
| 57 | + myserver.com | |
| 58 | + | |
| 59 | + # proxy regular and read only instance | |
| 60 | + proxy / 127.0.0.1:8001 | |
| 61 | + proxy /ro 127.0.0.1:8002 { without /ro } | |
| 62 | + | |
| 63 | + # reroute non-root user to read-only | |
| 64 | + # cm9... is the output of `printf "root:password" | base64` | |
| 65 | + rewrite { | |
| 66 | + if {>Authorization} not "Basic cm9vdDpwYXNzd29yZA==" | |
| 67 | + to /ro/{path} | |
| 68 | + } | |
| 69 | + | |
| 70 | + # gate access | |
| 71 | + basicauth / root password | |
| 72 | + basicauth / ro_user1 passworduser1 | |
| 73 | + basicauth / ro_user2 passworduser2 | |
| 74 | + ``` | |
| 75 | +</details> | |
| 76 | + | |
| 77 | +Caddy v2 Caddyfile | |
| 78 | + | |
| 79 | +```sh | |
| 80 | +myserver.com | |
| 81 | + | |
| 82 | +# gate access | |
| 83 | +basic_auth { | |
| 84 | + root $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG # password is "hiccup" | |
| 85 | + ro_user1 $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG # password is "hiccup" | |
| 86 | + ro_user2 $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG # password is "hiccup" | |
| 87 | +} | |
| 88 | + | |
| 89 | +# named matcher for root user | |
| 90 | +@isroot { | |
| 91 | + vars {http.auth.user.id} root | |
| 92 | +} | |
| 93 | + | |
| 94 | +# proxy regular and read only instance | |
| 95 | +handle @isroot { | |
| 96 | + reverse_proxy 127.0.0.1:8001 | |
| 97 | +} | |
| 98 | +# route non-root user to read only instance | |
| 99 | +handle { | |
| 100 | + reverse_proxy 127.0.0.1:8002 | |
| 101 | +} | |
| 102 | +``` | |
| 103 | + | |
| 104 | +then simply start the 2 wormhole instances, and caddy | |
| 105 | + | |
| 106 | +```sh | |
| 107 | +# start an instance in readonly | |
| 108 | +% ./wormhole -ro=true -p 8002 ~/folder & | |
| 109 | + | |
| 110 | +# start an instance with access to hidden files | |
| 111 | +% ./wormhole -k=false -p 8001 ~/folder & | |
| 112 | + | |
| 113 | +# start caddy | |
| 114 | +% ./caddy | |
| 115 | +``` | |
| 116 | + | |
| 117 | +### example 2 users on 2 different folders | |
| 118 | + | |
| 119 | +This sample Caddyfile will | |
| 120 | + + enable https on the domain myserver.com (http will be automatically redirected to https) | |
| 121 | + + password protect the access | |
| 122 | + + route user1 to own folder | |
| 123 | + + route user2 to own folder | |
| 124 | + + share a folder between 2 users with a symlink | |
| 125 | + | |
| 126 | +<details> | |
| 127 | + <summary>Legacy Caddy v1 Caddyfile</summary> | |
| 128 | + | |
| 129 | + ```sh | |
| 130 | + myserver.com | |
| 131 | + | |
| 132 | + proxy /user1 127.0.0.1:8001 { without /user1 } | |
| 133 | + proxy /user2 127.0.0.1:8002 { without /user2 } | |
| 134 | + | |
| 135 | + basicauth / user1 passworduser1 | |
| 136 | + basicauth / user2 passworduser2 | |
| 137 | + | |
| 138 | + rewrite { | |
| 139 | + if {>Authorization} is "Basic dXNlcjE6cGFzc3dvcmR1c2VyMQ==" | |
| 140 | + to /user1/{path} | |
| 141 | + } | |
| 142 | + | |
| 143 | + rewrite { | |
| 144 | + if {>Authorization} is "Basic dXNlcjI6cGFzc3dvcmR1c2VyMg==" | |
| 145 | + to /user2/{path} | |
| 146 | + } | |
| 147 | + ``` | |
| 148 | +</details> | |
| 149 | + | |
| 150 | +Caddy v2 Caddyfile | |
| 151 | + | |
| 152 | +```sh | |
| 153 | +myserver.com | |
| 154 | + | |
| 155 | +basic_auth { | |
| 156 | + user1 $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG # password is "hiccup" | |
| 157 | + user2 $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG # password is "hiccup" | |
| 158 | +} | |
| 159 | + | |
| 160 | +@user1auth { | |
| 161 | + vars {http.auth.user.id} user1 | |
| 162 | +} | |
| 163 | +handle @user1auth { | |
| 164 | + reverse_proxy 127.0.0.1:8001 | |
| 165 | +} | |
| 166 | + | |
| 167 | +@user2auth { | |
| 168 | + vars {http.auth.user.id} user2 | |
| 169 | +} | |
| 170 | +handle @user2auth { | |
| 171 | + reverse_proxy 127.0.0.1:8002 | |
| 172 | +} | |
| 173 | +``` | |
| 174 | + | |
| 175 | +start 2 wormhole instances, and caddy | |
| 176 | + | |
| 177 | +```sh | |
| 178 | +# create symlink to share folder between 2 users | |
| 179 | +% ln -s /path/shared test/user1 | |
| 180 | +% ln -s /path/shared test/user2 | |
| 181 | + | |
| 182 | +# start wormhole & caddy | |
| 183 | +% ./wormhole -p 8001 -symlinks=true test/user1 & | |
| 184 | +% ./wormhole -p 8002 -symlinks=true test/user2 & | |
| 185 | +% ./caddy | |
| 186 | +``` | |
| 187 | + | |
| 188 | +## nginx setup | |
| 189 | + | |
| 190 | +In order to allow for larger uploads, it's recommended to increase the maximum body size on your nginx config : | |
| 191 | + | |
| 192 | +``` | |
| 193 | +# increase maximum request size | |
| 194 | +client_max_body_size 100M; | |
| 195 | +``` | |
| @@ -0,0 +1,13 @@ | ||
| 1 | +[Unit] | |
| 2 | +Description=Wormhole service for mytestfolder | |
| 3 | +Wants=network-online.target | |
| 4 | +After=network-online.target | |
| 5 | + | |
| 6 | +# note: absolute paths are necessary here | |
| 7 | +[Service] | |
| 8 | +ExecStart=/usr/bin/wormhole /home/joe/mytestfolder | |
| 9 | +Restart=always | |
| 10 | +RestartSec=30 | |
| 11 | + | |
| 12 | +[Install] | |
| 13 | +WantedBy=multi-user.target | |
| @@ -0,0 +1 @@ | ||
| 1 | +hidden ! | |
| @@ -0,0 +1 @@ | ||
| 1 | +test | |
| @@ -0,0 +1 @@ | ||
| 1 | +<h1>404</h1> | |
Binary file not shown.
| @@ -0,0 +1 @@ | ||
| 1 | +B!!! | |
| @@ -0,0 +1 @@ | ||
| 1 | +exports.foo = 'baz' | |
Binary file not shown.
| @@ -0,0 +1 @@ | ||
| 1 | +exports.foo = 'baz' | |
| @@ -0,0 +1 @@ | ||
| 1 | +../hols | |
| @@ -0,0 +1 @@ | ||
| 1 | +index!!! | |
| @@ -0,0 +1,3 @@ | ||
| 1 | +# This file is an example of the Apache .types file format for describing mime-types. | |
| 2 | +# Other example: http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types | |
| 3 | +application/foo opml | |
Empty file added.
Empty file added.
Empty file added.
Empty file added.
Empty file added.
Empty file added.
Empty file added.
Empty file added.
Empty file added.
Empty file added.
Empty file added.
Empty file added.
Empty file added.
| @@ -0,0 +1 @@ | ||
| 1 | +fancy! | |
| @@ -0,0 +1 @@ | ||
| 1 | +ecstatic | |
Empty file added.
| @@ -0,0 +1 @@ | ||
| 1 | +ecstatic | |
| @@ -0,0 +1 @@ | ||
| 1 | +I'm hidden by my directory! | |
| @@ -0,0 +1 @@ | ||
| 1 | +console.log('C!!!') | |
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -0,0 +1,4 @@ | ||
| 1 | +It should generate an href of <a href="/show-dir-href-encoding/aname+aplus.txt">.. | |
| 2 | + | |
| 3 | +http://stackoverflow.com/a/1006074/147079 | |
| 4 | +http://tools.ietf.org/html/rfc1738 | |
| @@ -0,0 +1 @@ | ||
| 1 | +<b>e!!</b> | |
| @@ -0,0 +1 @@ | ||
| 1 | +<b>spacious!!</b> | |
| @@ -0,0 +1 @@ | ||
| 1 | +<b>file!!</b> | |
| @@ -0,0 +1 @@ | ||
| 1 | +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 180 180"><image width="180" height="180" href="data:image/png;base64,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"/></svg> | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +# wormhole UI | |
| 2 | + | |
| 3 | +Plain JS, CSS, and HTML for the file browser. No npm. No bundler. | |
| 4 | +The Go server embeds these files at build time. | |
| @@ -0,0 +1,925 @@ | ||
| 1 | +/* eslint-env browser */ | |
| 2 | +/* eslint-disable no-multi-str */ | |
| 3 | + | |
| 4 | +function cancelDefault (e) { | |
| 5 | + e.preventDefault() | |
| 6 | + e.stopPropagation() | |
| 7 | +} | |
| 8 | + | |
| 9 | +const warningMsg = () => 'Leaving will interrupt transfer?\n' | |
| 10 | +const rmMsg = () => !confirm('Remove file?\n') | |
| 11 | +const ensureMove = () => !confirm('move items?') | |
| 12 | +const isRo = () => window.ro | |
| 13 | + | |
| 14 | +// DOM elements | |
| 15 | +const upBarName = document.getElementById('upBarName') | |
| 16 | +const upBarPc = document.getElementById('upBarPc') | |
| 17 | +const upGrid = document.getElementById('drop-grid') | |
| 18 | +const pics = document.getElementById('pics') | |
| 19 | +const picsHolder = document.getElementById('picsHolder') | |
| 20 | +const video = document.getElementById('video') | |
| 21 | +const videoHolder = document.getElementById('videoHolder') | |
| 22 | +const manualUpload = document.getElementById('clickupload') | |
| 23 | +const pdf = document.getElementById('pdf') | |
| 24 | +const help = document.getElementById('help') | |
| 25 | +const okBadge = document.getElementById('ok') | |
| 26 | +const sadBadge = document.getElementById('sad') | |
| 27 | +const pageTitle = document.head.querySelector('title') | |
| 28 | +const pageH1 = document.body.querySelector('h1') | |
| 29 | +const editor = document.getElementById('text-editor') | |
| 30 | +const crossIcon = document.getElementById('quitAll') | |
| 31 | +const toast = document.getElementById('toast') | |
| 32 | +const table = document.getElementById('linkTable') | |
| 33 | +const helpMsg = document.getElementById('help_message') | |
| 34 | + | |
| 35 | +const transparentPixel = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=' | |
| 36 | + | |
| 37 | +// helpers | |
| 38 | +let allA | |
| 39 | +let imgsIndex | |
| 40 | +let allImgs | |
| 41 | +const decode = a => decodeURIComponent(a).replace(location.origin, '') | |
| 42 | +const getArrowSelected = () => document.querySelector('.arrow-selected') | |
| 43 | +const getASelected = () => !getArrowSelected() ? false : getArrowSelected().parentElement.parentElement.querySelectorAll('a')[0] | |
| 44 | +const prependPath = a => a.startsWith('/') ? a : decodeURI(location.pathname) + a | |
| 45 | +const prevent = e => e.preventDefault() | |
| 46 | +const flicker = w => w.classList.remove('runFade') || void w.offsetWidth || w.classList.add('runFade') // eslint-disable-line | |
| 47 | + | |
| 48 | +const encodeURIHash = e => encodeURI(e).replaceAll('#', '%23') | |
| 49 | + | |
| 50 | +// Manual upload | |
| 51 | +manualUpload.addEventListener('change', () => Array.from(manualUpload.files).forEach(f => isDupe(f.name) || postFile(f, '/' + f.name)), false) | |
| 52 | + | |
| 53 | +// Soft nav | |
| 54 | +async function browseTo (href, flickerDone, skipHistory) { | |
| 55 | + try { | |
| 56 | + const r = await fetch(href, { credentials: 'include' }) | |
| 57 | + const t = await r.text() | |
| 58 | + const parsed = new DOMParser().parseFromString(t, 'text/html') | |
| 59 | + | |
| 60 | + table.innerHTML = parsed.getElementById('linkTable').innerHTML | |
| 61 | + const title = parsed.head.querySelector('title').innerText | |
| 62 | + // check if is current path - if so skip following | |
| 63 | + if (pageTitle.innerText !== title) { | |
| 64 | + if (!skipHistory) { | |
| 65 | + const escaped = encodeURIHash(window.extraPath + title) | |
| 66 | + history.pushState({}, '', escaped) | |
| 67 | + } | |
| 68 | + pageTitle.innerText = title | |
| 69 | + pageH1.innerText = '.' + title | |
| 70 | + setTitle() | |
| 71 | + } | |
| 72 | + | |
| 73 | + init() | |
| 74 | + if (flickerDone) flicker(okBadge) | |
| 75 | + } catch (error) { | |
| 76 | + flicker(sadBadge) | |
| 77 | + } | |
| 78 | +} | |
| 79 | + | |
| 80 | +window.onClickLink = e => { | |
| 81 | + const a = e ? e.target : getASelected() | |
| 82 | + | |
| 83 | + if (e) { | |
| 84 | + setCursorTo(e.target.innerText) | |
| 85 | + } | |
| 86 | + | |
| 87 | + // always force download if ctrl pressed (also covers zipping folders) | |
| 88 | + if (e && e.ctrlKey) { | |
| 89 | + dl(a) | |
| 90 | + return false | |
| 91 | + } | |
| 92 | + | |
| 93 | + // follow dirs | |
| 94 | + if (isFolder(a)) { | |
| 95 | + browseTo(a.href) | |
| 96 | + return false | |
| 97 | + // enable notepad if relevant | |
| 98 | + } else if (!window.ro && isTextFile(a.innerText) && !isEditorMode()) { | |
| 99 | + padOn(a) | |
| 100 | + return false | |
| 101 | + // toggle picture carousel | |
| 102 | + } else if (isPic(a.href) && !isPicMode()) { | |
| 103 | + picsOn(a.href) | |
| 104 | + return false | |
| 105 | + // toggle videos mode | |
| 106 | + } else if (isVideo(a.href) && !isVideoMode()) { | |
| 107 | + videoOn(a.href) | |
| 108 | + return false | |
| 109 | + // let html be displayed naturally | |
| 110 | + } else if (a.innerText.endsWith('.html')) { | |
| 111 | + return true | |
| 112 | + } else if (isPdf(a.href)) { | |
| 113 | + openPDF(a.href) | |
| 114 | + return false | |
| 115 | + } | |
| 116 | + | |
| 117 | + // else just force download | |
| 118 | + dl(a) | |
| 119 | + return false | |
| 120 | +} | |
| 121 | + | |
| 122 | +let softStatePushed | |
| 123 | +function pushSoftState (d) { | |
| 124 | + if (softStatePushed) { return } | |
| 125 | + softStatePushed = true | |
| 126 | + history.pushState({}, '', encodeURIHash(d)) | |
| 127 | +} | |
| 128 | + | |
| 129 | +const refresh = () => browseTo(location.href, true) | |
| 130 | + | |
| 131 | +const softPrev = () => history.replaceState({}, '', location.href.split('/').slice(0, -1).join('/') + '/') | |
| 132 | + | |
| 133 | +const isAtExtraPath = url => location.origin + window.extraPath + '/../' === url | |
| 134 | +const prevPage = (url, skipHistory) => window.quitAll() || isAtExtraPath(url) || browseTo(url, false, skipHistory) | |
| 135 | + | |
| 136 | +window.onpopstate = () => prevPage(location.href, true) | |
| 137 | + | |
| 138 | +// RPC | |
| 139 | +function upload (id, what, path, cbDone, cbErr, cbUpdate) { | |
| 140 | + const xhr = new XMLHttpRequest() | |
| 141 | + xhr.open('POST', location.origin + window.extraPath + '/post') | |
| 142 | + xhr.setRequestHeader('wormhole-path', path) | |
| 143 | + xhr.upload.addEventListener('load', cbDone) | |
| 144 | + xhr.upload.addEventListener('progress', cbUpdate) | |
| 145 | + xhr.upload.addEventListener('error', cbErr) | |
| 146 | + xhr.upload.id = id | |
| 147 | + xhr.send(what) | |
| 148 | +} | |
| 149 | + | |
| 150 | +function rpc (call, args, cb) { | |
| 151 | + console.log('RPC', call, args) | |
| 152 | + const xhr = new XMLHttpRequest() | |
| 153 | + xhr.open('POST', location.origin + window.extraPath + '/rpc') | |
| 154 | + xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8') | |
| 155 | + xhr.send(JSON.stringify({ call, args })) | |
| 156 | + xhr.onload = cb | |
| 157 | + xhr.onerror = () => flicker(sadBadge) | |
| 158 | +} | |
| 159 | + | |
| 160 | +const mkdirCall = (path, cb) => rpc('mkdirp', [prependPath(path)], cb) | |
| 161 | +const rmCall = (path1, cb) => rpc('rm', [prependPath(path1)], cb) | |
| 162 | +const mvCall = (path1, path2, cb) => rpc('mv', [path1, path2], cb) | |
| 163 | +const sumCall = (path, type, cb) => rpc('sum', [prependPath(path), type], cb) | |
| 164 | + | |
| 165 | +// File upload | |
| 166 | +let totalDone = 0 | |
| 167 | +let totalUploads = 0 | |
| 168 | +let totalUploadsSize = 0 | |
| 169 | +let totalUploadedSize = [] | |
| 170 | + | |
| 171 | +const dupe = test => allA.find(a => a.innerHTML.replace('/', '') === test) | |
| 172 | +const isDupe = t => dupe(t) ? alert(t + ' already already exists') || true : false | |
| 173 | + | |
| 174 | +function shouldRefresh () { | |
| 175 | + totalDone += 1 | |
| 176 | + if (totalUploads === totalDone) { | |
| 177 | + window.onbeforeunload = null | |
| 178 | + console.log('done uploading ' + totalDone + ' files') | |
| 179 | + totalDone = 0 | |
| 180 | + totalUploads = 0 | |
| 181 | + totalUploadsSize = 0 | |
| 182 | + totalUploadedSize = [] | |
| 183 | + upBarPc.style.display = upBarName.style.display = 'none' | |
| 184 | + table.classList.remove('uploading-table') | |
| 185 | + setTimeout(refresh, 200) | |
| 186 | + } | |
| 187 | +} | |
| 188 | + | |
| 189 | +function updatePercent (ev) { | |
| 190 | + totalUploadedSize[ev.target.id] = ev.loaded | |
| 191 | + const ttlDone = totalUploadedSize.reduce((s, x) => s + x) | |
| 192 | + const pc = Math.floor(100 * ttlDone / totalUploadsSize) + '%' | |
| 193 | + upBarPc.innerText = pc | |
| 194 | + upBarPc.style.width = pc | |
| 195 | +} | |
| 196 | + | |
| 197 | +function postFile (file, path) { | |
| 198 | + if (window.ro) return | |
| 199 | + path = decodeURI(location.pathname).slice(0, -1) + path | |
| 200 | + window.onbeforeunload = warningMsg | |
| 201 | + | |
| 202 | + table.classList.add('uploading-table') | |
| 203 | + upBarPc.style.display = upBarName.style.display = 'block' | |
| 204 | + totalUploads += 1 | |
| 205 | + totalUploadsSize += file.size | |
| 206 | + upBarName.innerText = totalUploads > 1 ? totalUploads + ' files' : file.name | |
| 207 | + | |
| 208 | + const formData = new FormData() | |
| 209 | + formData.append(file.name, file) | |
| 210 | + upload(totalUploads, formData, encodeURIComponent(path), shouldRefresh, null, updatePercent) | |
| 211 | +} | |
| 212 | + | |
| 213 | +const parseDomFolder = f => f.createReader().readEntries(e => e.forEach(i => parseDomItem(i))) | |
| 214 | + | |
| 215 | +function parseDomItem (domFile, shoudCheckDupes) { | |
| 216 | + if (shoudCheckDupes && isDupe(domFile.name)) { | |
| 217 | + return | |
| 218 | + } | |
| 219 | + if (domFile.isFile) { | |
| 220 | + domFile.file(f => postFile(f, domFile.fullPath)) | |
| 221 | + } else { | |
| 222 | + // remove absolute path | |
| 223 | + const f = domFile.fullPath.startsWith('/') ? domFile.fullPath.slice(1) : domFile.fullPath | |
| 224 | + mkdirCall(f, () => parseDomFolder(domFile)) | |
| 225 | + } | |
| 226 | +} | |
| 227 | + | |
| 228 | +function pushEntry (entry) { | |
| 229 | + if (!entry.webkitGetAsEntry && !entry.getAsEntry) { | |
| 230 | + return alert('Unsupported browser ! Please update to chrome/firefox.') | |
| 231 | + } else { | |
| 232 | + entry = entry.webkitGetAsEntry() || entry.getAsEntry() | |
| 233 | + } | |
| 234 | + | |
| 235 | + parseDomItem(entry, true) | |
| 236 | +} | |
| 237 | + | |
| 238 | +window.titleClick = function (e) { | |
| 239 | + const p = Array.from(document.querySelector('h1').childNodes).map(k => k.innerText) | |
| 240 | + const i = p.findIndex(s => s === e.target.innerText) | |
| 241 | + const dst = p.slice(0, i + 1).join('').slice(1) | |
| 242 | + const target = location.origin + window.extraPath + encodeURIHash(dst) | |
| 243 | + browseTo(target, false) | |
| 244 | +} | |
| 245 | + | |
| 246 | +// Move files and folders | |
| 247 | +const isFolder = e => e && e.href && e.innerText.endsWith('/') | |
| 248 | + | |
| 249 | +const setBackgroundLinks = t => { t.classList.add('highlight') } | |
| 250 | + | |
| 251 | +const getLink = () => document.querySelector('.highlight') || {} | |
| 252 | + | |
| 253 | +const resetBackgroundLinks = () => { try { getLink().classList.remove('highlight') } catch(e) { /* */ } } // eslint-disable-line | |
| 254 | + | |
| 255 | +// Not the nicest - sometimes, upon hover, firefox reports nodeName === '#text', and chrome reports nodeName === 'A'... | |
| 256 | +const getClosestRow = t => t.nodeName === '#text' ? t.parentElement.parentElement : t.nodeName === 'A' ? t.parentElement : t | |
| 257 | + | |
| 258 | +let draggingSrc | |
| 259 | + | |
| 260 | +upGrid.ondragend = upGrid.ondragexit = upGrid.ondragleave = e => { | |
| 261 | + cancelDefault(e) | |
| 262 | + upGrid.style.display = 'none' | |
| 263 | +} | |
| 264 | + | |
| 265 | +// Handle hover | |
| 266 | +document.ondragenter = e => { | |
| 267 | + if (isEditorMode() || isPicMode() || window.ro) { return } | |
| 268 | + cancelDefault(e) | |
| 269 | + resetBackgroundLinks() | |
| 270 | + | |
| 271 | + // Display upload grid when uploading new elements | |
| 272 | + if (!draggingSrc) { | |
| 273 | + upGrid.style.display = 'flex' | |
| 274 | + e.dataTransfer.dropEffect = 'copy' | |
| 275 | + // Or highlight entry if drag and drop | |
| 276 | + } else if (draggingSrc) { | |
| 277 | + const t = getClosestRow(e.target) | |
| 278 | + isFolder(t.firstChild) && setBackgroundLinks(t) | |
| 279 | + } | |
| 280 | +} | |
| 281 | + | |
| 282 | +document.ondragstart = e => { draggingSrc = e.target.innerHTML } | |
| 283 | + | |
| 284 | +document.ondragend = e => resetBackgroundLinks() | |
| 285 | + | |
| 286 | +document.ondragover = e => { | |
| 287 | + cancelDefault(e) | |
| 288 | + return false | |
| 289 | +} | |
| 290 | + | |
| 291 | +// Handle drop | |
| 292 | +document.ondrop = e => { | |
| 293 | + if (window.ro) return | |
| 294 | + | |
| 295 | + cancelDefault(e) | |
| 296 | + upGrid.style.display = 'none' | |
| 297 | + const t = getLink().firstChild | |
| 298 | + | |
| 299 | + // move to a folder | |
| 300 | + if (draggingSrc && t) { | |
| 301 | + const dest = t.innerHTML + draggingSrc | |
| 302 | + ensureMove() || mvCall(prependPath(draggingSrc), prependPath(dest), refresh) | |
| 303 | + // ... or upload | |
| 304 | + } else if (e.dataTransfer.items.length) { | |
| 305 | + Array.from(e.dataTransfer.items).forEach(pushEntry) | |
| 306 | + } | |
| 307 | + | |
| 308 | + resetBackgroundLinks() | |
| 309 | + draggingSrc = null | |
| 310 | + return false | |
| 311 | +} | |
| 312 | + | |
| 313 | +// Notepad | |
| 314 | +const isEditorMode = () => editor.style.display === 'block' | |
| 315 | +const textTypes = ['.txt', '.rtf', '.md', '.markdown', '.log', '.yaml', '.yml'] | |
| 316 | +const isTextFile = src => src && textTypes.find(type => src.toLocaleLowerCase().includes(type)) | |
| 317 | +let fileEdited | |
| 318 | + | |
| 319 | +function saveText (quitting) { | |
| 320 | + const formData = new FormData() | |
| 321 | + formData.append(fileEdited, editor.value) | |
| 322 | + const path = encodeURIComponent(decodeURI(location.pathname) + fileEdited) | |
| 323 | + upload(0, formData, path, () => { | |
| 324 | + toast.style.display = 'none' | |
| 325 | + if (!quitting) return | |
| 326 | + clearInterval(window.padTimer) | |
| 327 | + window.onbeforeunload = null | |
| 328 | + resetView() | |
| 329 | + softPrev() | |
| 330 | + refresh() | |
| 331 | + }, () => { | |
| 332 | + toast.style.display = 'block' | |
| 333 | + if (!quitting) return | |
| 334 | + alert('cant save!\r\nleave window open to resume saving\r\nwhen connection back up') | |
| 335 | + }) | |
| 336 | +} | |
| 337 | + | |
| 338 | +function padOff () { | |
| 339 | + if (!isEditorMode()) { return } | |
| 340 | + saveText(true) | |
| 341 | + return true | |
| 342 | +} | |
| 343 | + | |
| 344 | +async function padOn (a) { | |
| 345 | + if (a) { | |
| 346 | + try { | |
| 347 | + fileEdited = a.innerHTML | |
| 348 | + const f = await fetch(a.href, { | |
| 349 | + credentials: 'include', | |
| 350 | + headers: new Headers({ pragma: 'no-cache', 'cache-control': 'no-cache' }) | |
| 351 | + }) | |
| 352 | + editor.value = await f.text() | |
| 353 | + } catch (error) { | |
| 354 | + return alert('cant read file') | |
| 355 | + } | |
| 356 | + } else { | |
| 357 | + fileEdited = prompt('new filename', '') | |
| 358 | + if (!fileEdited) { return } | |
| 359 | + fileEdited = isTextFile(fileEdited) ? fileEdited : fileEdited + '.txt' | |
| 360 | + if (isDupe(fileEdited)) { return } | |
| 361 | + editor.value = '' | |
| 362 | + } | |
| 363 | + | |
| 364 | + console.log('editing file', fileEdited) | |
| 365 | + setCursorTo(fileEdited) | |
| 366 | + editor.style.display = crossIcon.style.display = 'block' | |
| 367 | + table.style.display = 'none' | |
| 368 | + editor.focus() | |
| 369 | + window.onbeforeunload = warningMsg | |
| 370 | + window.padTimer = setInterval(saveText, 5000) | |
| 371 | + pushSoftState('?editor=' + fileEdited) | |
| 372 | +} | |
| 373 | + | |
| 374 | +window.displayPad = padOn | |
| 375 | + | |
| 376 | +// quit pictures or editor | |
| 377 | +function resetView () { | |
| 378 | + softStatePushed = false | |
| 379 | + table.style.display = 'table' | |
| 380 | + picsHolder.src = transparentPixel | |
| 381 | + videoHolder.src = '' | |
| 382 | + pdf.innerHTML = '' | |
| 383 | + editor.style.display = pics.style.display = video.style.display = pdf.style.display = crossIcon.style.display = 'none' | |
| 384 | + scrollToArrow() | |
| 385 | +} | |
| 386 | + | |
| 387 | +window.quitAll = () => helpOff() || sumsOff() || picsOff() || videosOff() || padOff() || pdfOff() | |
| 388 | + | |
| 389 | +// Mkdir icon | |
| 390 | +window.mkdirBtn = function () { | |
| 391 | + const folder = prompt('new folder name', '') | |
| 392 | + if (folder && !isDupe(folder)) { | |
| 393 | + mkdirCall(folder, refresh) | |
| 394 | + } | |
| 395 | +} | |
| 396 | + | |
| 397 | +// Icon click handler | |
| 398 | +const getBtnA = e => e.target.closest('tr').querySelector('a') | |
| 399 | + | |
| 400 | +window.rm = e => { | |
| 401 | + if (window.ro) return true | |
| 402 | + clearTimeout(window.clickToken) | |
| 403 | + const target = e.key ? getASelected() : getBtnA(e) | |
| 404 | + if (target.innerText === '../') return | |
| 405 | + if (rmMsg()) return | |
| 406 | + | |
| 407 | + moveArrow() | |
| 408 | + rmCall(decode(target.href), refresh) | |
| 409 | +} | |
| 410 | + | |
| 411 | +window.rename = (e, commit) => { | |
| 412 | + if (window.ro) return true | |
| 413 | + clearTimeout(window.clickToken) | |
| 414 | + | |
| 415 | + if (!commit) { | |
| 416 | + window.clickToken = setTimeout(window.rename, 300, e, true) | |
| 417 | + return | |
| 418 | + } | |
| 419 | + | |
| 420 | + const target = e.key ? getASelected() : getBtnA(e) | |
| 421 | + if (target.innerText === '../') return | |
| 422 | + const chg = prompt('rename to', target.innerText) | |
| 423 | + if (chg && !isDupe(chg)) { | |
| 424 | + mvCall(prependPath(target.innerText), prependPath(chg), refresh) | |
| 425 | + } | |
| 426 | +} | |
| 427 | + | |
| 428 | +function aboveBelowRightin (el) { | |
| 429 | + const itemPos = el.getBoundingClientRect() | |
| 430 | + return itemPos.top < 0 ? -1 : itemPos.bottom > window.innerHeight ? 1 : 0 | |
| 431 | +} | |
| 432 | + | |
| 433 | +function scrollToArrow () { | |
| 434 | + const el = getASelected() | |
| 435 | + while (1) { | |
| 436 | + const pos = aboveBelowRightin(el) | |
| 437 | + if (pos === -1) { | |
| 438 | + scrollBy(0, -300) | |
| 439 | + } else if (pos === 1) { | |
| 440 | + scrollBy(0, 300) | |
| 441 | + } else { | |
| 442 | + break | |
| 443 | + } | |
| 444 | + } | |
| 445 | +} | |
| 446 | + | |
| 447 | +function clearArrowSelected () { | |
| 448 | + const arr = getArrowSelected() | |
| 449 | + if (!arr) { return } | |
| 450 | + arr.classList.remove('arrow-selected') | |
| 451 | +} | |
| 452 | + | |
| 453 | +window.setCursorTo = setCursorTo | |
| 454 | +function setCursorTo (where) { | |
| 455 | + if (!where) return false | |
| 456 | + clearArrowSelected() | |
| 457 | + let a = allA.find(el => el.innerText === where || el.innerText === where + '/') | |
| 458 | + | |
| 459 | + if (!a) { | |
| 460 | + if (allA[0].innerText === '../') { | |
| 461 | + a = allA[1] || allA[0] | |
| 462 | + } else { | |
| 463 | + a = allA[0] | |
| 464 | + } | |
| 465 | + } | |
| 466 | + | |
| 467 | + const icon = a.parentElement.parentElement.querySelectorAll('.arrow-icon')[0] | |
| 468 | + icon.classList.add('arrow-selected') | |
| 469 | + scrollToArrow() | |
| 470 | + storeArrow(where) | |
| 471 | + return true | |
| 472 | +} | |
| 473 | + | |
| 474 | +function moveArrow (down) { | |
| 475 | + const all = Array.from(document.querySelectorAll('.arrow-icon')) | |
| 476 | + let i = all.findIndex(el => el.classList.contains('arrow-selected')) | |
| 477 | + | |
| 478 | + clearArrowSelected() | |
| 479 | + | |
| 480 | + if (down) { | |
| 481 | + i = all[i + 1] ? i + 1 : 0 | |
| 482 | + } else { | |
| 483 | + i = all[i - 1] ? i - 1 : all.length - 1 | |
| 484 | + } | |
| 485 | + | |
| 486 | + all[i].classList.add('arrow-selected') | |
| 487 | + storeArrow(getASelected().innerText) | |
| 488 | + scrollToArrow() | |
| 489 | +} | |
| 490 | + | |
| 491 | +const storeArrow = src => localStorage.setItem('last-selected' + window.extraPath + location.pathname, src) | |
| 492 | + | |
| 493 | +const isTop = () => window.scrollY === 0 | |
| 494 | +const isBottom = () => (window.innerHeight + window.scrollY) >= document.body.offsetHeight | |
| 495 | +const hasScroll = () => table.clientHeight > window.innerHeight | |
| 496 | + | |
| 497 | +function movePage (up) { | |
| 498 | + const current = getASelected().href | |
| 499 | + | |
| 500 | + if (!hasScroll()) return | |
| 501 | + | |
| 502 | + if (!up) { | |
| 503 | + const i = allA.findIndex(e => aboveBelowRightin(e) === 1) | |
| 504 | + if (isTop() && current !== allA[i - 1].href) { | |
| 505 | + return setCursorTo(allA[i - 1].innerText) | |
| 506 | + } else if (isBottom() && current !== allA[allA.length - 1].href) { | |
| 507 | + return setCursorTo(allA[allA.length - 1].innerText) | |
| 508 | + } | |
| 509 | + | |
| 510 | + if (!allA[i - 1]) return | |
| 511 | + setCursorTo(allA[i - 1].innerText) | |
| 512 | + scrollBy(0, window.innerHeight - 100) | |
| 513 | + } else { | |
| 514 | + const i = allA.findIndex(e => aboveBelowRightin(e) === 0) | |
| 515 | + if (isTop() && current !== allA[0].href) { | |
| 516 | + return setCursorTo(allA[0].innerText) | |
| 517 | + } else if (isBottom() && current !== allA[i].href) { | |
| 518 | + return setCursorTo(allA[i].innerText) | |
| 519 | + } | |
| 520 | + | |
| 521 | + scrollBy(0, -(window.innerHeight - 100)) | |
| 522 | + setCursorTo(allA[i].innerText) | |
| 523 | + } | |
| 524 | +} | |
| 525 | + | |
| 526 | +// Pictures carousel | |
| 527 | +const picTypes = ['.jpg', '.jpeg', '.png', '.gif'] | |
| 528 | +const isPic = src => src && picTypes.find(type => src.toLocaleLowerCase().includes(type)) | |
| 529 | +const isPicMode = () => pics.style.display === 'flex' | |
| 530 | +window.picsNav = () => picsNav(true) | |
| 531 | + | |
| 532 | +function setImage () { | |
| 533 | + const src = allImgs[imgsIndex] | |
| 534 | + picsHolder.src = src | |
| 535 | + const name = src.split('/').pop() | |
| 536 | + setCursorTo(decodeURI(name)) | |
| 537 | + history.replaceState({}, '', encodeURIHash(name)) | |
| 538 | +} | |
| 539 | + | |
| 540 | +function picsOn (href) { | |
| 541 | + imgsIndex = allImgs.findIndex(el => el.includes(href)) | |
| 542 | + setImage() | |
| 543 | + table.style.display = 'none' | |
| 544 | + crossIcon.style.display = 'block' | |
| 545 | + pics.style.display = 'flex' | |
| 546 | + const name = href.split('/').pop() | |
| 547 | + pushSoftState(name) | |
| 548 | + return true | |
| 549 | +} | |
| 550 | + | |
| 551 | +function picsOff () { | |
| 552 | + if (!isPicMode()) { return } | |
| 553 | + resetView() | |
| 554 | + softPrev() | |
| 555 | + return true | |
| 556 | +} | |
| 557 | + | |
| 558 | +function picsNav (down) { | |
| 559 | + if (!isPicMode()) { return false } | |
| 560 | + | |
| 561 | + if (down) { | |
| 562 | + imgsIndex = allImgs[imgsIndex + 1] ? imgsIndex + 1 : 0 | |
| 563 | + } else { | |
| 564 | + imgsIndex = allImgs[imgsIndex - 1] ? imgsIndex - 1 : allImgs.length - 1 | |
| 565 | + } | |
| 566 | + | |
| 567 | + setImage() | |
| 568 | + return true | |
| 569 | +} | |
| 570 | + | |
| 571 | +let picsTouchStart = 0 | |
| 572 | + | |
| 573 | +picsHolder.addEventListener('touchstart', e => { | |
| 574 | + picsTouchStart = e.changedTouches[0].screenX | |
| 575 | +}, false) | |
| 576 | + | |
| 577 | +picsHolder.addEventListener('touchend', e => { | |
| 578 | + if (e.changedTouches[0].screenX < picsTouchStart) { | |
| 579 | + picsNav(true) | |
| 580 | + } else if (e.changedTouches[0].screenX > picsTouchStart) { | |
| 581 | + picsNav(false) | |
| 582 | + } | |
| 583 | +}, false) | |
| 584 | + | |
| 585 | +// Video player | |
| 586 | +const videosTypes = ['.mp4', '.webm', '.ogv', '.ogg', '.mp3', '.flac', '.wav'] | |
| 587 | +const isVideo = src => src && videosTypes.find(type => src.toLocaleLowerCase().includes(type)) | |
| 588 | +const isVideoMode = () => video.style.display === 'flex' | |
| 589 | +const videoFs = () => video.requestFullscreen() | |
| 590 | +const videoFf = future => { videoHolder.currentTime += future ? 10 : -10 } | |
| 591 | +const videoSound = up => { videoHolder.volume += up ? 0.1 : -0.1 } | |
| 592 | +videoHolder.oncanplay = () => videoHolder.play() | |
| 593 | + | |
| 594 | +async function videoOn (src) { | |
| 595 | + const name = src.split('/').pop() | |
| 596 | + table.style.display = 'none' | |
| 597 | + crossIcon.style.display = 'block' | |
| 598 | + video.style.display = 'flex' | |
| 599 | + videoHolder.pause() | |
| 600 | + | |
| 601 | + const time = localStorage.getItem('video-time' + src) | |
| 602 | + videoHolder.currentTime = parseInt(time) || 0 | |
| 603 | + | |
| 604 | + videoHolder.src = src | |
| 605 | + pushSoftState(decodeURI(name)) | |
| 606 | + return true | |
| 607 | +} | |
| 608 | + | |
| 609 | +function videosOff () { | |
| 610 | + if (!isVideoMode()) { return } | |
| 611 | + localStorage.setItem('video-time' + videoHolder.src, videoHolder.currentTime) | |
| 612 | + resetView() | |
| 613 | + softPrev() | |
| 614 | + return true | |
| 615 | +} | |
| 616 | + | |
| 617 | +window.videodl = function () { | |
| 618 | + dl(getASelected()) | |
| 619 | +} | |
| 620 | + | |
| 621 | +// PDF Viewer | |
| 622 | +const pdfTypes = ['.pdf'] | |
| 623 | +const isPdf = src => src && pdfTypes.find(type => src.toLocaleLowerCase().includes(type)) | |
| 624 | +const isPdfMode = () => pdf.style.display === 'flex' | |
| 625 | +function openPDF (src) { | |
| 626 | + const name = src.split('/').pop() | |
| 627 | + table.style.display = 'none' | |
| 628 | + crossIcon.style.display = 'block' | |
| 629 | + pdf.style.display = 'flex' | |
| 630 | + const pdfEmbed = document.createElement('embed') | |
| 631 | + pdfEmbed.setAttribute('src', src + '#toolbar=0') | |
| 632 | + pdfEmbed.setAttribute('type', 'application/pdf') | |
| 633 | + pdf.appendChild(pdfEmbed) | |
| 634 | + pushSoftState(decodeURI(name)) | |
| 635 | + return false | |
| 636 | +} | |
| 637 | + | |
| 638 | +function pdfOff () { | |
| 639 | + if (!isPdfMode()) { return } | |
| 640 | + resetView() | |
| 641 | + softPrev() | |
| 642 | + return true | |
| 643 | +} | |
| 644 | + | |
| 645 | +// help | |
| 646 | +const isHelpMode = () => help.style.display === 'block' | |
| 647 | + | |
| 648 | +const helpToggle = () => isHelpMode() ? helpOff() : helpOn() | |
| 649 | + | |
| 650 | +function helpOn () { | |
| 651 | + help.style.display = 'block' | |
| 652 | + table.style.display = 'none' | |
| 653 | +} | |
| 654 | + | |
| 655 | +window.helpOff = helpOff | |
| 656 | +function helpOff () { | |
| 657 | + if (!isHelpMode()) return | |
| 658 | + help.style.display = 'none' | |
| 659 | + table.style.display = 'table' | |
| 660 | + return true | |
| 661 | +} | |
| 662 | + | |
| 663 | +// checksums | |
| 664 | +function getSum (type) { | |
| 665 | + upBarPc.style.display = 'block' | |
| 666 | + upBarPc.innerText = 'computing checksum...' | |
| 667 | + upBarPc.style.width = '100%' | |
| 668 | + sumsOff() | |
| 669 | + sumCall(getASelected().innerText, type, loaded => { | |
| 670 | + navigator.clipboard.writeText(loaded.target.responseText) | |
| 671 | + upBarPc.style.display = 'none' | |
| 672 | + flicker(okBadge) | |
| 673 | + }) | |
| 674 | +} | |
| 675 | + | |
| 676 | +const isSumsMode = () => sums.style.display === 'block' | |
| 677 | + | |
| 678 | +const sumsToggle = () => isSumsMode() ? sumsOff() : sumsOn() | |
| 679 | + | |
| 680 | +function sumsOn () { | |
| 681 | + if (isFolder(getASelected())) { | |
| 682 | + alert('cannot checksum a directory') | |
| 683 | + return | |
| 684 | + } | |
| 685 | + sums.style.display = 'block' | |
| 686 | + table.style.display = 'none' | |
| 687 | +} | |
| 688 | + | |
| 689 | +window.sumsOff = sumsOff | |
| 690 | +function sumsOff () { | |
| 691 | + if (!isSumsMode()) return | |
| 692 | + sums.style.display = 'none' | |
| 693 | + table.style.display = 'table' | |
| 694 | + return true | |
| 695 | +} | |
| 696 | + | |
| 697 | +// Paste handler | |
| 698 | +const cuts = [] | |
| 699 | +function onPaste () { | |
| 700 | + if (!cuts.length) { return refresh() } | |
| 701 | + const a = getASelected() | |
| 702 | + const root = cuts.pop() | |
| 703 | + const filename = root.split('/').pop() | |
| 704 | + const pwd = decodeURIComponent(location.pathname) | |
| 705 | + const dest = isFolder(a) ? pwd + a.innerHTML : pwd | |
| 706 | + mvCall(root, dest + filename, onPaste) | |
| 707 | +} | |
| 708 | + | |
| 709 | +function onCut () { | |
| 710 | + const a = getASelected() | |
| 711 | + a.classList.add('linkSelected') | |
| 712 | + cuts.push(prependPath(decode(a.href))) | |
| 713 | +} | |
| 714 | + | |
| 715 | +function dl (a) { | |
| 716 | + const orig = a.onclick | |
| 717 | + a.onclick = '' | |
| 718 | + | |
| 719 | + // download as zip if folder | |
| 720 | + if (isFolder(a)) { | |
| 721 | + const loc = a.href | |
| 722 | + a.href = window.extraPath + '/zip?zipPath=' + encodeURIComponent(prependPath(a.innerText)) + '&zipName=' + encodeURIComponent(a.innerText.slice(0, -1)) | |
| 723 | + a.click() | |
| 724 | + a.href = loc | |
| 725 | + } else { | |
| 726 | + a.download = a.innerText | |
| 727 | + a.click() | |
| 728 | + a.download = '' | |
| 729 | + } | |
| 730 | + | |
| 731 | + a.onclick = orig | |
| 732 | +} | |
| 733 | + | |
| 734 | +// Kb handler | |
| 735 | +let typedPath = '' | |
| 736 | +let typedToken = null | |
| 737 | + | |
| 738 | +function cpPath () { | |
| 739 | + const t = document.createElement('textarea') | |
| 740 | + t.value = getASelected().href | |
| 741 | + document.body.appendChild(t) | |
| 742 | + t.select() | |
| 743 | + document.execCommand('copy') | |
| 744 | + document.body.removeChild(t) | |
| 745 | +} | |
| 746 | + | |
| 747 | +document.body.addEventListener('keydown', e => { | |
| 748 | + if (e.code === 'Escape') { | |
| 749 | + return resetBackgroundLinks() || window.quitAll() | |
| 750 | + } | |
| 751 | + | |
| 752 | + if (isHelpMode()) { return prevent(e) || window.quitAll() } | |
| 753 | + | |
| 754 | + if (isEditorMode()) { return } | |
| 755 | + | |
| 756 | + if (isPicMode()) { | |
| 757 | + switch (e.code) { | |
| 758 | + case 'ArrowLeft': | |
| 759 | + case 'ArrowUp': | |
| 760 | + return prevent(e) || picsNav(false) | |
| 761 | + | |
| 762 | + case 'Enter': | |
| 763 | + case 'Tab': | |
| 764 | + case 'ArrowRight': | |
| 765 | + case 'ArrowDown': | |
| 766 | + return prevent(e) || picsNav(true) | |
| 767 | + } | |
| 768 | + return | |
| 769 | + } | |
| 770 | + | |
| 771 | + if (isVideoMode()) { | |
| 772 | + switch (e.code) { | |
| 773 | + case 'ArrowDown': | |
| 774 | + case 'ArrowUp': | |
| 775 | + return prevent(e) || videoSound(e.code === 'ArrowUp') | |
| 776 | + | |
| 777 | + case 'ArrowLeft': | |
| 778 | + case 'ArrowRight': | |
| 779 | + return prevent(e) || videoFf(e.code === 'ArrowRight') | |
| 780 | + | |
| 781 | + case 'KeyF': | |
| 782 | + return prevent(e) || videoFs() | |
| 783 | + } | |
| 784 | + return | |
| 785 | + } | |
| 786 | + | |
| 787 | + // Modifier keys | |
| 788 | + if (!e.shiftKey) { | |
| 789 | + if (e.ctrlKey || e.metaKey) { | |
| 790 | + switch (e.code) { | |
| 791 | + case 'KeyC': | |
| 792 | + return prevent(e) || isRo() || cpPath() | |
| 793 | + | |
| 794 | + case 'KeyH': | |
| 795 | + return prevent(e) || isRo() || helpToggle() | |
| 796 | + | |
| 797 | + case 'KeyZ': | |
| 798 | + return prevent(e) || isRo() || sumsToggle() | |
| 799 | + | |
| 800 | + case 'KeyX': | |
| 801 | + return prevent(e) || isRo() || onCut() | |
| 802 | + | |
| 803 | + case 'KeyR': | |
| 804 | + return prevent(e) || refresh() | |
| 805 | + | |
| 806 | + case 'KeyV': | |
| 807 | + return prevent(e) || isRo() || ensureMove() || onPaste() | |
| 808 | + | |
| 809 | + case 'Backspace': | |
| 810 | + return prevent(e) || isRo() || window.rm(e) | |
| 811 | + | |
| 812 | + case 'KeyE': | |
| 813 | + return prevent(e) || isRo() || window.rename(e) | |
| 814 | + | |
| 815 | + case 'KeyM': | |
| 816 | + return prevent(e) || isRo() || window.mkdirBtn() | |
| 817 | + | |
| 818 | + case 'KeyU': | |
| 819 | + return prevent(e) || isRo() || manualUpload.click() | |
| 820 | + | |
| 821 | + case 'Enter': | |
| 822 | + case 'ArrowRight': | |
| 823 | + return prevent(e) || dl(getASelected()) | |
| 824 | + } | |
| 825 | + } else if (isSumsMode()) { | |
| 826 | + switch (e.code) { | |
| 827 | + case 'Digit1': | |
| 828 | + return prevent(e) || isRo() || getSum('sha1') | |
| 829 | + | |
| 830 | + case 'Digit2': | |
| 831 | + return prevent(e) || isRo() || getSum('sha256') | |
| 832 | + | |
| 833 | + case 'Digit3': | |
| 834 | + return prevent(e) || isRo() || getSum('sha512') | |
| 835 | + | |
| 836 | + case 'Digit5': | |
| 837 | + return prevent(e) || isRo() || getSum('md5') | |
| 838 | + } | |
| 839 | + } | |
| 840 | + } else { | |
| 841 | + // Workaround Firefox requirement for transient activation | |
| 842 | + // https://developer.mozilla.org/en-US/docs/Web/Security/User_activation | |
| 843 | + // Firefox requires user interaction (that is not reserved by the user agent) | |
| 844 | + // before a file picker can be displayed. This means that ctrl/meta are not | |
| 845 | + // usable as modifiers until the user clicks the page or presses another | |
| 846 | + // non-modifier key. To work around this, the shift key can be used, instead. | |
| 847 | + if (e.code == 'KeyU') { | |
| 848 | + return prevent(e) || isRo() || manualUpload.click() | |
| 849 | + } | |
| 850 | + } | |
| 851 | + | |
| 852 | + switch (e.code) { | |
| 853 | + case 'Tab': | |
| 854 | + case 'ArrowDown': | |
| 855 | + return prevent(e) || moveArrow(true) | |
| 856 | + | |
| 857 | + case 'ArrowUp': | |
| 858 | + return prevent(e) || moveArrow(false) | |
| 859 | + | |
| 860 | + case 'Enter': | |
| 861 | + case 'ArrowRight': | |
| 862 | + return prevent(e) || window.onClickLink() | |
| 863 | + | |
| 864 | + case 'ArrowLeft': | |
| 865 | + return prevent(e) || prevPage(location.href + '../') | |
| 866 | + | |
| 867 | + case 'PageDown': | |
| 868 | + case 'PageUp': | |
| 869 | + return prevent(e) || movePage(e.key === 'PageUp') | |
| 870 | + | |
| 871 | + case 'Space': | |
| 872 | + return prevent(e) || movePage(e.shiftKey) | |
| 873 | + } | |
| 874 | + | |
| 875 | + // text search | |
| 876 | + if (e.code.includes('Key') && !e.ctrlKey && !e.metaKey) { | |
| 877 | + typedPath += e.code.replace('Key', '').toLocaleLowerCase() | |
| 878 | + clearTimeout(typedToken) | |
| 879 | + typedToken = setTimeout(() => { typedPath = '' }, 1000) | |
| 880 | + | |
| 881 | + const a = allA.find(el => el.innerText.toLocaleLowerCase().startsWith(typedPath)) || allA.find(el => el.innerText.toLocaleLowerCase().includes(typedPath)) | |
| 882 | + if (!a) { return } | |
| 883 | + setCursorTo(a.innerText) | |
| 884 | + } | |
| 885 | +}, false) | |
| 886 | + | |
| 887 | +function setTitle () { | |
| 888 | + pageH1.innerHTML = '<span>' + pageH1.innerText.split('/').join('/</span><span>') + '</span>' | |
| 889 | +} | |
| 890 | + | |
| 891 | +function init () { | |
| 892 | + allA = Array.from(document.querySelectorAll('a.list-links')) | |
| 893 | + allImgs = allA.map(el => el.href).filter(isPic) | |
| 894 | + imgsIndex = softStatePushed = 0 | |
| 895 | + | |
| 896 | + const successRestore = setCursorTo(localStorage.getItem('last-selected' + window.extraPath + location.pathname)) | |
| 897 | + if (!successRestore) { | |
| 898 | + const entries = table.querySelectorAll('.arrow-icon') | |
| 899 | + entries.length === 1 ? entries[0].classList.add('arrow-selected') : entries[1].classList.add('arrow-selected') | |
| 900 | + } | |
| 901 | + | |
| 902 | + setTitle() | |
| 903 | + scrollToArrow() | |
| 904 | + console.log('browsed to ' + location.href) | |
| 905 | + | |
| 906 | + if (cuts.length) { | |
| 907 | + const match = allA.filter(a => cuts.find(c => c === decode(a.href))) | |
| 908 | + match.forEach(m => m.classList.add('linkSelected')) | |
| 909 | + } | |
| 910 | + | |
| 911 | + // restore editor if was queried | |
| 912 | + if (location.search.includes('?editor=')) { | |
| 913 | + const cleanURL = location.href.replace('?editor=', '') | |
| 914 | + const matchingA = allA.find(a => a.href === cleanURL) | |
| 915 | + padOn(matchingA) | |
| 916 | + } | |
| 917 | + | |
| 918 | + // check if we're at root path | |
| 919 | + if (location.pathname === window.extraPath + '/') { | |
| 920 | + helpMsg.style.display = 'block' | |
| 921 | + } else { | |
| 922 | + helpMsg.style.display = 'none' | |
| 923 | + } | |
| 924 | +} | |
| 925 | +init() | |
| @@ -0,0 +1,645 @@ | ||
| 1 | +/* ============================================================================ | |
| 2 | + * FILE: style.css | |
| 3 | + * ROLE: STYLESHEET | |
| 4 | + * PURPOSE: Wormhole file browser — krisyotam.com cell-strip design language | |
| 5 | + * ========================================================================== */ | |
| 6 | + | |
| 7 | +/* -- Variables ------------------------------------------------------------- */ | |
| 8 | +:root { | |
| 9 | + --bg: hsl(0 0% 98%); | |
| 10 | + --bg-muted: hsl(0 0% 96%); | |
| 11 | + --bg-muted-30: hsla(0,0%,92%,0.3); | |
| 12 | + --fg: hsl(0 0% 20%); | |
| 13 | + --fg-muted: hsl(0 0% 50%); | |
| 14 | + --border: hsl(0 0% 88%); | |
| 15 | + --hover: hsla(0,0%,92%,0.3); | |
| 16 | + --highlight: hsl(0 0% 90%); | |
| 17 | + --accent: hsl(0 0% 10%); | |
| 18 | + --icon-invert: 0%; | |
| 19 | + color-scheme: light; | |
| 20 | +} | |
| 21 | + | |
| 22 | +[data-theme="dark"] { | |
| 23 | + --bg: hsl(0 0% 7%); | |
| 24 | + --bg-muted: hsl(0 0% 12%); | |
| 25 | + --bg-muted-30: hsla(0,0%,15%,0.3); | |
| 26 | + --fg: hsl(0 0% 90%); | |
| 27 | + --fg-muted: hsl(0 0% 55%); | |
| 28 | + --border: hsl(0 0% 18%); | |
| 29 | + --hover: hsla(0,0%,20%,0.3); | |
| 30 | + --highlight: hsl(0 0% 20%); | |
| 31 | + --accent: hsl(0 0% 90%); | |
| 32 | + --icon-invert: 85%; | |
| 33 | + color-scheme: dark; | |
| 34 | +} | |
| 35 | + | |
| 36 | +@media (prefers-color-scheme: dark) { | |
| 37 | + :root:not([data-theme="light"]) { | |
| 38 | + --bg: hsl(0 0% 7%); | |
| 39 | + --bg-muted: hsl(0 0% 12%); | |
| 40 | + --bg-muted-30: hsla(0,0%,15%,0.3); | |
| 41 | + --fg: hsl(0 0% 90%); | |
| 42 | + --fg-muted: hsl(0 0% 55%); | |
| 43 | + --border: hsl(0 0% 18%); | |
| 44 | + --hover: hsla(0,0%,20%,0.3); | |
| 45 | + --highlight: hsl(0 0% 20%); | |
| 46 | + --accent: hsl(0 0% 90%); | |
| 47 | + --icon-invert: 85%; | |
| 48 | + color-scheme: dark; | |
| 49 | + } | |
| 50 | +} | |
| 51 | + | |
| 52 | +/* -- Base ------------------------------------------------------------------ */ | |
| 53 | +@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=IBM+Plex+Sans:wght@400;500;600&display=swap'); | |
| 54 | + | |
| 55 | +* { box-sizing: border-box; } | |
| 56 | + | |
| 57 | +::-webkit-scrollbar { background: var(--bg); width: 6px; } | |
| 58 | +::-webkit-scrollbar-thumb { background: var(--border); } | |
| 59 | + | |
| 60 | +html { | |
| 61 | + color: var(--fg); | |
| 62 | + background-color: var(--bg); | |
| 63 | + font-size: 15px; | |
| 64 | + font-family: 'IBM Plex Sans', -apple-system, Helvetica, Arial, sans-serif; | |
| 65 | + line-height: 1.65; | |
| 66 | + overflow-x: hidden; | |
| 67 | + scrollbar-color: var(--border) var(--bg); | |
| 68 | + -webkit-tap-highlight-color: transparent; | |
| 69 | + -webkit-font-smoothing: antialiased; | |
| 70 | +} | |
| 71 | + | |
| 72 | +body { | |
| 73 | + margin: 0; | |
| 74 | + padding: 0; | |
| 75 | + min-height: 100vh; | |
| 76 | + display: flex; | |
| 77 | + align-items: center; | |
| 78 | + justify-content: center; | |
| 79 | +} | |
| 80 | + | |
| 81 | +a { | |
| 82 | + color: var(--fg); | |
| 83 | + text-decoration: none; | |
| 84 | + background-color: transparent !important; | |
| 85 | + outline: 0; | |
| 86 | +} | |
| 87 | + | |
| 88 | +a:hover { color: var(--accent); } | |
| 89 | + | |
| 90 | +a.linkSelected { border-bottom: 1px solid var(--fg); } | |
| 91 | + | |
| 92 | +div, i { background-size: cover !important; } | |
| 93 | + | |
| 94 | +/* -- Page Container (cell-strip article) ----------------------------------- */ | |
| 95 | +#page { | |
| 96 | + border: 1px solid var(--border); | |
| 97 | + width: 92vw; | |
| 98 | + max-width: 960px; | |
| 99 | + min-height: 60vh; | |
| 100 | + display: flex; | |
| 101 | + flex-direction: column; | |
| 102 | +} | |
| 103 | + | |
| 104 | +/* -- Rows ------------------------------------------------------------------ */ | |
| 105 | +.row { | |
| 106 | + display: flex; | |
| 107 | + align-items: stretch; | |
| 108 | + border-bottom: 1px solid var(--border); | |
| 109 | +} | |
| 110 | + | |
| 111 | +.row:last-child { border-bottom: none; } | |
| 112 | + | |
| 113 | +/* -- Cells ----------------------------------------------------------------- */ | |
| 114 | +.cell { | |
| 115 | + padding: 8px 12px; | |
| 116 | +} | |
| 117 | + | |
| 118 | +/* -- Header Row ------------------------------------------------------------ */ | |
| 119 | +.row-header { | |
| 120 | + min-height: 52px; | |
| 121 | +} | |
| 122 | + | |
| 123 | +.cell-label { | |
| 124 | + width: 56px; | |
| 125 | + display: flex; | |
| 126 | + align-items: center; | |
| 127 | + justify-content: center; | |
| 128 | + border-right: 1px solid var(--border); | |
| 129 | + background: var(--bg-muted-30); | |
| 130 | + flex-shrink: 0; | |
| 131 | + font-family: 'IBM Plex Mono', monospace; | |
| 132 | + font-size: 10px; | |
| 133 | + font-weight: 500; | |
| 134 | + text-transform: uppercase; | |
| 135 | + letter-spacing: 0.08em; | |
| 136 | + color: var(--fg-muted); | |
| 137 | +} | |
| 138 | + | |
| 139 | +.cell-path { | |
| 140 | + flex: 1; | |
| 141 | + display: flex; | |
| 142 | + align-items: center; | |
| 143 | + min-width: 0; | |
| 144 | + cursor: pointer; | |
| 145 | +} | |
| 146 | + | |
| 147 | +.cell-path h1 { | |
| 148 | + margin: 0; | |
| 149 | + font-family: 'IBM Plex Mono', monospace; | |
| 150 | + font-size: 13px; | |
| 151 | + font-weight: 500; | |
| 152 | + color: var(--fg); | |
| 153 | + letter-spacing: 0.01em; | |
| 154 | + white-space: nowrap; | |
| 155 | + overflow: hidden; | |
| 156 | + text-overflow: ellipsis; | |
| 157 | + display: block; | |
| 158 | + width: 100%; | |
| 159 | +} | |
| 160 | + | |
| 161 | +.cell-path h1 > span:hover { | |
| 162 | + color: var(--fg-muted); | |
| 163 | +} | |
| 164 | + | |
| 165 | +.cell-actions { | |
| 166 | + display: flex; | |
| 167 | + align-items: center; | |
| 168 | + justify-content: center; | |
| 169 | + border-left: 1px solid var(--border); | |
| 170 | + background: var(--bg-muted-30); | |
| 171 | + flex-shrink: 0; | |
| 172 | + width: 52px; | |
| 173 | + padding: 0; | |
| 174 | +} | |
| 175 | + | |
| 176 | +/* -- Column Headers -------------------------------------------------------- */ | |
| 177 | +.row-colheader { | |
| 178 | + background: var(--bg-muted-30); | |
| 179 | + font-family: 'IBM Plex Mono', monospace; | |
| 180 | + font-size: 10px; | |
| 181 | + font-weight: 500; | |
| 182 | + text-transform: uppercase; | |
| 183 | + letter-spacing: 0.08em; | |
| 184 | + color: var(--fg-muted); | |
| 185 | + min-height: 28px; | |
| 186 | +} | |
| 187 | + | |
| 188 | +.cell-col-icon { | |
| 189 | + width: 56px; | |
| 190 | + border-right: 1px solid var(--border); | |
| 191 | + flex-shrink: 0; | |
| 192 | +} | |
| 193 | + | |
| 194 | +.cell-col-size { | |
| 195 | + width: 72px; | |
| 196 | + text-align: right; | |
| 197 | + padding: 6px 12px; | |
| 198 | + border-right: 1px solid var(--border); | |
| 199 | + flex-shrink: 0; | |
| 200 | +} | |
| 201 | + | |
| 202 | +.cell-col-name { | |
| 203 | + flex: 1; | |
| 204 | + padding: 6px 12px; | |
| 205 | +} | |
| 206 | + | |
| 207 | +/* -- Listing --------------------------------------------------------------- */ | |
| 208 | +#listing { | |
| 209 | + flex: 1; | |
| 210 | + min-height: 120px; | |
| 211 | + border-bottom: 1px solid var(--border); | |
| 212 | +} | |
| 213 | + | |
| 214 | +/* -- Table ----------------------------------------------------------------- */ | |
| 215 | +table { | |
| 216 | + width: 100%; | |
| 217 | + border-collapse: collapse; | |
| 218 | +} | |
| 219 | + | |
| 220 | +tr { | |
| 221 | + line-height: 40px; | |
| 222 | + border-bottom: 1px solid var(--border); | |
| 223 | + transition: background-color 0.1s; | |
| 224 | +} | |
| 225 | + | |
| 226 | + | |
| 227 | +tr:hover { background-color: var(--hover); } | |
| 228 | + | |
| 229 | +td.iconRow { | |
| 230 | + text-align: center; | |
| 231 | + width: 56px; | |
| 232 | + padding: 0; | |
| 233 | + border-right: 1px solid var(--border); | |
| 234 | +} | |
| 235 | + | |
| 236 | +.icon { | |
| 237 | + height: 18px; | |
| 238 | + width: 18px; | |
| 239 | + display: block; | |
| 240 | + cursor: pointer; | |
| 241 | + filter: grayscale(1) invert(var(--icon-invert)); | |
| 242 | + opacity: 0.5; | |
| 243 | + margin: 0 auto; | |
| 244 | +} | |
| 245 | + | |
| 246 | +td.file-size { | |
| 247 | + text-align: right; | |
| 248 | + width: 72px; | |
| 249 | + padding: 0 12px; | |
| 250 | + border-right: 1px solid var(--border); | |
| 251 | + font-family: 'IBM Plex Mono', monospace; | |
| 252 | + font-size: 12px; | |
| 253 | + color: var(--fg-muted); | |
| 254 | + white-space: nowrap; | |
| 255 | +} | |
| 256 | + | |
| 257 | +td.file-size code { font-family: inherit; } | |
| 258 | + | |
| 259 | +.arrow { | |
| 260 | + height: 14px; | |
| 261 | + width: 14px; | |
| 262 | + text-align: center; | |
| 263 | + padding-left: 8px; | |
| 264 | + filter: invert(var(--icon-invert)); | |
| 265 | + opacity: 0.25; | |
| 266 | + padding-bottom: 2px; | |
| 267 | +} | |
| 268 | + | |
| 269 | +.arrow-selected { | |
| 270 | + height: inherit; | |
| 271 | + opacity: 0.6; | |
| 272 | +} | |
| 273 | + | |
| 274 | +td.display-name { | |
| 275 | + padding-left: 12px; | |
| 276 | + text-overflow: ellipsis; | |
| 277 | + overflow-x: hidden; | |
| 278 | + white-space: nowrap; | |
| 279 | + max-width: 100px; | |
| 280 | + font-size: 15px; | |
| 281 | +} | |
| 282 | + | |
| 283 | +.highlight { background-color: var(--highlight); } | |
| 284 | + | |
| 285 | +/* -- Footer Row ------------------------------------------------------------ */ | |
| 286 | +.row-footer { | |
| 287 | + min-height: 32px; | |
| 288 | + font-family: 'IBM Plex Mono', monospace; | |
| 289 | + font-size: 11px; | |
| 290 | + color: var(--fg-muted); | |
| 291 | + background: var(--bg-muted-30); | |
| 292 | +} | |
| 293 | + | |
| 294 | +.cell-footer-help { | |
| 295 | + flex: 1; | |
| 296 | + display: flex; | |
| 297 | + align-items: center; | |
| 298 | + padding: 6px 12px; | |
| 299 | + opacity: 0.6; | |
| 300 | +} | |
| 301 | + | |
| 302 | +.cell-footer-brand { | |
| 303 | + display: flex; | |
| 304 | + align-items: center; | |
| 305 | + padding: 6px 12px; | |
| 306 | + border-left: 1px solid var(--border); | |
| 307 | + letter-spacing: 0.02em; | |
| 308 | +} | |
| 309 | + | |
| 310 | +/* -- Theme Toggle Button --------------------------------------------------- */ | |
| 311 | +#themeToggle { | |
| 312 | + cursor: pointer; | |
| 313 | + background: none; | |
| 314 | + border: none; | |
| 315 | + outline: none; | |
| 316 | + color: var(--fg-muted); | |
| 317 | + padding: 4px; | |
| 318 | + line-height: 1; | |
| 319 | + display: flex; | |
| 320 | + align-items: center; | |
| 321 | + justify-content: center; | |
| 322 | + transition: color 0.15s; | |
| 323 | + -webkit-appearance: none; | |
| 324 | + appearance: none; | |
| 325 | +} | |
| 326 | + | |
| 327 | +#themeToggle:hover { | |
| 328 | + color: var(--fg); | |
| 329 | +} | |
| 330 | + | |
| 331 | +#themeToggle:focus { | |
| 332 | + outline: none; | |
| 333 | +} | |
| 334 | + | |
| 335 | +/* -- Action Icons ---------------------------------------------------------- */ | |
| 336 | +#icHolder { display: none; } | |
| 337 | + | |
| 338 | +.ic { | |
| 339 | + width: 24px; | |
| 340 | + height: 24px; | |
| 341 | + cursor: pointer; | |
| 342 | + display: inline-block; | |
| 343 | + overflow: hidden; | |
| 344 | + color: transparent; | |
| 345 | + opacity: 0.45; | |
| 346 | + filter: grayscale(1) invert(var(--icon-invert)); | |
| 347 | + transition: opacity 0.15s; | |
| 348 | +} | |
| 349 | + | |
| 350 | +.ic:hover { opacity: 1; } | |
| 351 | + | |
| 352 | +/* -- Upload Bar ------------------------------------------------------------ */ | |
| 353 | +.uploading-table { padding-bottom: 80px; } | |
| 354 | + | |
| 355 | +.bar { | |
| 356 | + width: 100%; | |
| 357 | + left: 0; | |
| 358 | + right: 0; | |
| 359 | + position: fixed; | |
| 360 | + bottom: 0; | |
| 361 | + max-height: 50%; | |
| 362 | + overflow-x: hidden; | |
| 363 | + background-color: var(--bg); | |
| 364 | + border-top: 1px solid var(--border); | |
| 365 | +} | |
| 366 | + | |
| 367 | +.barName { | |
| 368 | + width: 100%; | |
| 369 | + font-size: 12px; | |
| 370 | + color: var(--fg-muted); | |
| 371 | + font-family: 'IBM Plex Mono', monospace; | |
| 372 | +} | |
| 373 | + | |
| 374 | +.barPc { | |
| 375 | + width: 1%; | |
| 376 | + height: 28px; | |
| 377 | + text-align: center; | |
| 378 | + line-height: 28px; | |
| 379 | + min-width: 30px; | |
| 380 | + margin-top: 4px; | |
| 381 | + color: var(--bg); | |
| 382 | + background-color: var(--accent) !important; | |
| 383 | + margin-bottom: 4px; | |
| 384 | + font-family: 'IBM Plex Mono', monospace; | |
| 385 | + font-size: 11px; | |
| 386 | +} | |
| 387 | + | |
| 388 | +/* -- Drop Zone ------------------------------------------------------------- */ | |
| 389 | +#drop-grid { | |
| 390 | + display: none !important; | |
| 391 | +} | |
| 392 | + | |
| 393 | +/* -- Text Editor ----------------------------------------------------------- */ | |
| 394 | +#text-editor { | |
| 395 | + font-family: 'IBM Plex Mono', monospace; | |
| 396 | + font-size: 13px; | |
| 397 | + line-height: 1.6; | |
| 398 | + background-color: var(--bg); | |
| 399 | + color: var(--fg); | |
| 400 | + position: fixed; | |
| 401 | + inset: 0; | |
| 402 | + z-index: 100; | |
| 403 | + overflow-y: auto; | |
| 404 | + word-wrap: anywhere; | |
| 405 | + padding: 40px; | |
| 406 | + outline: none; | |
| 407 | + width: -moz-available; | |
| 408 | + width: -webkit-fill-available; | |
| 409 | + height: 100%; | |
| 410 | + border: none; | |
| 411 | + resize: none; | |
| 412 | + box-sizing: border-box; | |
| 413 | +} | |
| 414 | + | |
| 415 | +/* -- Media Viewers --------------------------------------------------------- */ | |
| 416 | +#pics, #pdf, #video { | |
| 417 | + user-select: none; | |
| 418 | + -webkit-tap-highlight-color: transparent; | |
| 419 | + justify-content: center; | |
| 420 | + align-items: center; | |
| 421 | + position: fixed; | |
| 422 | + background-color: hsl(0 0% 0%); | |
| 423 | + inset: 0; | |
| 424 | + overflow: hidden; | |
| 425 | + z-index: 99; | |
| 426 | +} | |
| 427 | + | |
| 428 | +#picsHolder { | |
| 429 | + user-select: none; | |
| 430 | + height: 100%; | |
| 431 | + width: 100%; | |
| 432 | + cursor: pointer; | |
| 433 | + object-fit: contain; | |
| 434 | + image-orientation: from-image; | |
| 435 | +} | |
| 436 | + | |
| 437 | +#pdf embed { width: 100%; height: 100%; } | |
| 438 | + | |
| 439 | +#videoHolder { | |
| 440 | + height: auto; | |
| 441 | + max-height: 100%; | |
| 442 | + width: 100%; | |
| 443 | + cursor: pointer; | |
| 444 | + object-fit: contain; | |
| 445 | +} | |
| 446 | + | |
| 447 | +#video-dl { | |
| 448 | + position: fixed; | |
| 449 | + right: 8px; | |
| 450 | + top: 80px; | |
| 451 | + width: 50px; | |
| 452 | + height: 50px; | |
| 453 | + cursor: pointer; | |
| 454 | + z-index: 101; | |
| 455 | + filter: invert(var(--icon-invert)); | |
| 456 | +} | |
| 457 | + | |
| 458 | +/* -- Close Button ---------------------------------------------------------- */ | |
| 459 | +#quitAll { | |
| 460 | + position: fixed; | |
| 461 | + right: 16px; | |
| 462 | + top: 16px; | |
| 463 | + width: 28px; | |
| 464 | + height: 28px; | |
| 465 | + cursor: pointer; | |
| 466 | + z-index: 101; | |
| 467 | + filter: invert(var(--icon-invert)); | |
| 468 | + opacity: 0.7; | |
| 469 | +} | |
| 470 | + | |
| 471 | +#quitAll:hover { opacity: 1; } | |
| 472 | + | |
| 473 | +/* -- Toast ----------------------------------------------------------------- */ | |
| 474 | +#toast { | |
| 475 | + top: 7px; | |
| 476 | + position: absolute; | |
| 477 | + right: 70px; | |
| 478 | + font-style: normal; | |
| 479 | + font-family: 'IBM Plex Mono', monospace; | |
| 480 | + font-size: 12px; | |
| 481 | + color: var(--fg-muted); | |
| 482 | + width: max-content; | |
| 483 | +} | |
| 484 | + | |
| 485 | +/* -- Notifications --------------------------------------------------------- */ | |
| 486 | +@keyframes fade { | |
| 487 | + 0% { opacity: 0; } | |
| 488 | + 20% { opacity: 1; } | |
| 489 | + 80% { opacity: 1; } | |
| 490 | + 100% { opacity: 0; } | |
| 491 | +} | |
| 492 | + | |
| 493 | +.runFade { animation: fade 3s 1; } | |
| 494 | + | |
| 495 | +.notif { | |
| 496 | + opacity: 0; | |
| 497 | + width: 60px; | |
| 498 | + height: 60px; | |
| 499 | + position: fixed; | |
| 500 | + bottom: 24px; | |
| 501 | + right: 24px; | |
| 502 | + filter: invert(var(--icon-invert)); | |
| 503 | +} | |
| 504 | + | |
| 505 | +/* -- Help / Sums Overlays -------------------------------------------------- */ | |
| 506 | +#helpTable, | |
| 507 | +#sumsTable { | |
| 508 | + border-collapse: collapse; | |
| 509 | + width: 70%; | |
| 510 | + max-width: 600px; | |
| 511 | + margin: 80px auto; | |
| 512 | +} | |
| 513 | + | |
| 514 | +#helpTable td, | |
| 515 | +#sumsTable td { | |
| 516 | + width: 200px; | |
| 517 | + padding: 8px 12px; | |
| 518 | + border: 1px solid var(--border); | |
| 519 | + color: var(--fg); | |
| 520 | + font-size: 13px; | |
| 521 | + font-family: 'IBM Plex Sans', sans-serif; | |
| 522 | +} | |
| 523 | + | |
| 524 | +#help, | |
| 525 | +#sums { | |
| 526 | + background-color: var(--bg); | |
| 527 | + position: absolute; | |
| 528 | + inset: 0; | |
| 529 | + overflow-y: scroll; | |
| 530 | + z-index: 99; | |
| 531 | + font-size: 1em !important; | |
| 532 | +} | |
| 533 | + | |
| 534 | +/* -- Legacy (hidden, replaced by footer) ----------------------------------- */ | |
| 535 | +#help_message { display: none; } | |
| 536 | + | |
| 537 | +/* -- Mobile ---------------------------------------------------------------- */ | |
| 538 | +@media (max-width: 800px) { | |
| 539 | + html { | |
| 540 | + font-size: 13px !important; | |
| 541 | + user-select: none; | |
| 542 | + } | |
| 543 | + body { | |
| 544 | + padding: 8px 0 !important; | |
| 545 | + align-items: flex-start !important; | |
| 546 | + } | |
| 547 | + #page { | |
| 548 | + border-left: none; | |
| 549 | + border-right: none; | |
| 550 | + width: 100vw !important; | |
| 551 | + min-height: 90vh; | |
| 552 | + } | |
| 553 | + .cell-label { width: 40px; font-size: 9px; } | |
| 554 | + td.iconRow { width: 40px; } | |
| 555 | + .cell-col-icon { width: 40px; } | |
| 556 | + .cell-col-size, td.file-size { display: none; } | |
| 557 | + .arrow { display: none !important; } | |
| 558 | + td.display-name { padding-left: 10px !important; } | |
| 559 | + tr { line-height: 2.6em !important; } | |
| 560 | + .cell-path h1 { font-size: 12px; } | |
| 561 | + .row-footer { font-size: 10px; } | |
| 562 | + .cell-footer-help { display: none; } | |
| 563 | +} | |
| 564 | + | |
| 565 | +/* -- Icon Classes ---------------------------------------------------------- */ | |
| 566 | +.manualUp { | |
| 567 | + display: none; | |
| 568 | + margin: unset; | |
| 569 | +} | |
| 570 | + | |
| 571 | + | |
| 572 | +.icon-large-sad-server { | |
| 573 | + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAHlBMVEUAAACAgIDAwMAAAAD///8A/wAAgAD/AACAgAD//wAxEuVaAAAAAXRSTlMAQObYZgAAAAFiS0dEBI9o2VEAAAAHdElNRQfiBhoALCLL7Xg4AAAAy0lEQVQ4y+WSPQ7CMAyFHbUSa38G5iLBHCkXKBUnQOICDMwgBnMDuEJvi00TKM3L0IWFN/rLs/zsEP2BSiCtm1WsxgqoAChtwqAAGgSYXaztC3So0xywPuwx2JyOM1uNwWRN1RtMUnQ/alVTAtgEqPWw6OJ6WHJA358lGx7GMs0swHwWsGCeAikJEAwst8d9bCh8kCVzz3zxQQpDbe53x6qwyDYnm/lQ0oevIaHNPj2dAEdIHM8aBgbDDgZo0dDQokWwEV8DlqE0sjwBakRwyV+dKtoAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTgtMDYtMjZUMDA6NDQ6MzQtMDQ6MDBDgg+9AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE4LTA2LTI2VDAwOjQ0OjM0LTA0OjAwMt+3AQAAAABJRU5ErkJggg=="); | |
| 574 | +} | |
| 575 | + | |
| 576 | +.icon-large-ok { | |
| 577 | + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAElBMVEUAAAAAAAAAgACAgID////AwMAbJqpVAAAAAXRSTlMAQObYZgAAAAFiS0dEBI9o2VEAAAAHdElNRQfiBhoANRyRjMyLAAAArElEQVQoz22R0Q3EIAiGNbfAeU4gad+9sMGFLtC4/yyngBaa+tCELz+fFkJ4PimltyljAoCSrvoDfBbRGkC74qyh3AIauQIacaCajo1+ANmARqcHRP2T61LsdIgkWoUFrICCC7Bigo1OUUzQK1GslkaiGIDfsZMoICvokcODXRTjpXpvU0W9/T5WP7GhcDNF9EPN3GH2ooEQvrI51EAIL+y7zbgCg/Cx68eZ/wNuLjeFQzbY1AAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOC0wNi0yNlQwMDo1MzoyOC0wNDowMGvaoe4AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTgtMDYtMjZUMDA6NTM6MjgtMDQ6MDAahxlSAAAAAElFTkSuQmCC"); | |
| 578 | +} | |
| 579 | + | |
| 580 | +.icon-large-pad { | |
| 581 | + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAGFBMVEUAAAAAAACAgID////AwMAAAL8AAP///wCsLlssAAAAAXRSTlMAQObYZgAAAAFiS0dEAxEMTPIAAAAHdElNRQfiBhoAOCQMIApYAAAAvUlEQVQoz22Ryw7CIBREqYl7xlb3Pj7APpQ1iaZbXcjaxBTXhCi/bx+2XBomd8PJDDcMjCUc43DWab8rpunIIi/TcbYyCkovKdltBpbPGVAtuHjJqz6GDvUOI1d9DiPqpcMt6tsEkXyjwy04NWEkhWooEMD6wUnEGuBOHidEBXACrKsNuuf/A7Wr3AekD2trB1JQe4MzUvqIK4BMeofIV0OFIzBpOYG+qmxoLNJ6DNCSow528I6s//4EXu3xB1FNX4O6vHKSAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE4LTA2LTI2VDAwOjU2OjM2LTA0OjAwEWYRaQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOC0wNi0yNlQwMDo1NjozNi0wNDowMGA7qdUAAAAASUVORK5CYII="); | |
| 582 | +} | |
| 583 | + | |
| 584 | +.icon-large-upload { | |
| 585 | + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAXVBMVEUAAAAzzDPM/8z///8z/2YzmQAzZgAzZv8Amf8AAAAzzP+ZzP8AgADM//8z//8AMwAzzGaZmQD4+Pj//5kAZgD/zGYzmTP/zJmAgAAzMwDMZjPMmTP//wBmMwDwyqYv7S31AAAAAXRSTlMAQObYZgAAAAFiS0dEAxEMTPIAAAAHdElNRQfiBhoAOCALTc5BAAAB3klEQVRIx5WUCZKDIBBFAQXEbTTRQbPM/Y85TQMKClm+piplvcdv0YSQINSFfBQAWcHKsqSUc/EBbmnAgedCyje8Xxxxw782LI+zCPORVSWlesPj4qKqETdC3nA8R76uasBr+cJoCmHGdzwadSsxaaPpCiHcPJ6naLQqw3d9J2jEix/AW540msIYZnMMX/et5xnPCNjQC+SrvoUvlh8GzlWW73tcH+YRdk+tIFRqoq6jwPf+Diph5mcjJiEAThkNeQb4MF6uJmej6ShjrJymnR+GjU8YDfLwJk2Wr9DY+LNBi4DHSMHGOcjo418k5Es+TY6vxO94SUU5oUQDXg1QpDlEOBFEu3MOjJKCsfioUNBe0fNqBcfTZaMWdYkKXMUmIF8uAbSo0zx7A+GH9SPDwNqegWD4Oc6yx1dsAuHw61ku2ShsCRoImY4T6fCYlZ0oEMjEl8NG7rd6nVd9bAAjbrCOb1i1PgkkELR7VnpveCV4OLiH1W9dpiGaR5sGH5IQjvPgJKn/Disc9ke755UXzjQ23NTdJRb0NZ5ne16Pp8+HDSEYCPbFvMb0cfuTu3QwcoKOnpf+umFPRpjzSQpkzYe8z/OuPqC+E+5hbhAVXUmsGebx93eLLjzf1L1i/gHK3l4JVuA9nAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOC0wNi0yNlQwMDo1NjozMi0wNDowMOUpNXoAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTgtMDYtMjZUMDA6NTY6MzItMDQ6MDCUdI3GAAAAAElFTkSuQmCC"); | |
| 586 | + margin-bottom: 3px; | |
| 587 | +} | |
| 588 | + | |
| 589 | +.icon-folder { | |
| 590 | + height: 27px; | |
| 591 | + width: 27px; | |
| 592 | + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAIVBMVEUAAACZmQAICAj4+Pj//5n/+/DMzGYAAAD/zJkzMwD///9AsZvJAAAAAXRSTlMAQObYZgAAAAFiS0dECmjQ9FYAAAAHdElNRQfiBhgXARMJeV+TAAAAuElEQVQoz62QMQ7CQBADLyVlQKLmD/ABpC38AF5AgZSOR7i4kpbf4t3b4xLR4i6+sb1KKb+apf3qe7qY2fU0jIO5BjLdwhjI8dE0Nylx3uiZFS4YjfduAJADLCvCCNrKoAL2JajPyG0IZ9IQGzP1S2iSMdsMZgbshOIVsRsGs7WidzAm1IGaEfgNIugRxIJblXUQ/my1dXgaUaDLZDDm5HgiIvGqQyw7GJVOIO7oPzm1lN17o1f5gz69jIC7vf9PIQAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOC0wNi0yNFQyMzowMToxOS0wNDowMO2EXMYAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTgtMDYtMjRUMjM6MDE6MTktMDQ6MDCc2eR6AAAAAElFTkSuQmCC") !important; | |
| 593 | +} | |
| 594 | + | |
| 595 | +.icon-blank { | |
| 596 | + background-image: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2cHQiIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2cHQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxsaW5lYXJHcmFkaWVudCBpZD0iYSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4yNzUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjguMjY3NSIgeDI9IjguMjY3NSIgeGxpbms6aHJlZj0iI2EiIHkxPSIxNC4wMTY4NzUiIHkyPSIxNC42NzgzNDM3NSIvPjxsaW5lYXJHcmFkaWVudCBpZD0iYyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4zMTQiLz48c3RvcCBvZmZzZXQ9Ii4yMjIiIHN0b3Atb3BhY2l0eT0iLjI3NSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1vcGFjaXR5PSIwIi8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9ImQiIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC0uMDM5MDcgLS4wNzAzMiAwIDI0Ljc0MiAzMS43OTgpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZSIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLjAzOTA3IC4wNzAzMiAwIC05LjE5NyAtMy43NjQpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZiIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLS4wMzkwNyAuMDcwMzIgMCAtOS4xOTcgMzEuNzk4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHI9IjIxLjE2NyIgeGxpbms6aHJlZj0iI2MiLz48cmFkaWFsR3JhZGllbnQgaWQ9ImciIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC4wMzkwNyAtLjA3MDMyIDAgMjQuNzQyIC0zLjc2NCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjciIHhsaW5rOmhyZWY9IiNjIi8+PGxpbmVhckdyYWRpZW50IGlkPSJoIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC4wMDgyNjkwMiAuMDAwMTI3MyAtLjAwMDEyNzMgLjAwODI2OTAyIDAgMCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iOTQwIiB4Mj0iOTQwIiB5MT0iMTY5NS4xMTgiIHkyPSIxNzUuMTE4Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNlMGUwZTAiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNlZWUiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iaSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguMDE0OTUgMCAwIC4wNTQ3IDcuNTIxIC04LjI2MykiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMjQ5LjE4OSIgeDI9IjI0OS4xODkiIHhsaW5rOmhyZWY9IiNhIiB5MT0iMjM2LjUyNCIgeTI9IjI1NC42NjciLz48bGluZWFyR3JhZGllbnQgaWQ9ImoiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzllOWU5ZSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2JkYmRiZCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJrIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjEwLjU4MzM0Mzc1IiB4Mj0iMTAuNTgzMzQzNzUiIHhsaW5rOmhyZWY9IiNqIiB5MT0iMTQuMDE2ODc1IiB5Mj0iMS40NDkxNTYyNSIvPjxsaW5lYXJHcmFkaWVudCBpZD0ibCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIyNTQiIHgyPSIyNTQiIHhsaW5rOmhyZWY9IiNqIiB5MT0iMjMzLjUiIHkyPSItMTY4LjY2NyIvPjxwYXRoIGQ9Im0zLjk2OSAxNC4wMTZoNy42MXYuNjY0aC03LjYxem0wIDAiIGZpbGw9InVybCgjYikiLz48cGF0aCBkPSJtMTEuNTc4IDE0LjAxNmgxLjMydi0uNjZoLTEuMzJ6bTAgMCIgZmlsbD0idXJsKCNkKSIvPjxwYXRoIGQ9Im0zLjk2OSAxNC4wMTZoLTEuMzI0di42NjRoMS4zMjR6bTAgMCIgZmlsbD0idXJsKCNlKSIvPjxwYXRoIGQ9Im0zLjk2OSAxNC4wMTZoLTEuMzI0di0uNjZoMS4zMjR6bTAgMCIgZmlsbD0idXJsKCNmKSIvPjxwYXRoIGQ9Im0xMS41NzggMTQuMDE2aDEuMzJ2LjY2NGgtMS4zMnptMCAwIiBmaWxsPSJ1cmwoI2cpIi8+PHBhdGggZD0ibTkuMjcgMS40NWgtNS45NjFhLjY2LjY2IDAgMCAwIC0uNjY0LjY2djExLjI0NWMwIC4zNjguMjk2LjY2LjY2NC42Nmg4LjkzYy4zNjYgMCAuNjYtLjI5Mi42Ni0uNjZ2LTguMjc3em0wIDAiIGZpbGw9InVybCgjaCkiLz48cGF0aCBkPSJtOS4yNjIgNC41MDhoMy4xNGwuNDk2LjQ5NnYuNjZoLTMuNjM2em0wIDAiIGZpbGw9InVybCgjaSkiLz48cGF0aCBkPSJtMi42NDUgMTMuMDk4di4yNTdjMCAuMzY4LjI5Ni42Ni42NjQuNjZoOC45M2MuMzY2IDAgLjY2LS4yOTIuNjYtLjY2di0uMjU3YS42Ni42NiAwIDAgMSAtLjY2LjY2NGgtOC45M2EuNjY0LjY2NCAwIDAgMSAtLjY2NC0uNjY0em0wIDAiIGZpbGwtb3BhY2l0eT0iLjIzNSIvPjxwYXRoIGQ9Im0zLjMwOSAxLjQ1YS42Ni42NiAwIDAgMCAtLjY2NC42NnYuMjVjMC0uMzY4LjI5Ni0uNjY1LjY2NC0uNjY1aDUuOTZsMy42MyAzLjYzdi0uMjQ3bC0zLjYzMS0zLjYyOHptMCAwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii43ODQiLz48cGF0aCBkPSJtMTIuODk4IDUuMDc4LTMuNjI4LTMuNjI4djIuOTY1YzAgLjM2Ny4yOTYuNjY0LjY2NC42NjR6bTAgMCIgZmlsbD0idXJsKCNrKSIvPjxwYXRoIGQ9Im05LjI3IDQuMjU4di4xNTZjMCAuMzY3LjI5Ni42NjQuNjY0LjY2NGgyLjk2NGwtLjE1Ni0uMTU2aC0yLjgwOGEuNjY0LjY2NCAwIDAgMSAtLjY2NC0uNjY0em0wIDAiIGZpbGwtb3BhY2l0eT0iLjIzNSIvPjxwYXRoIGQ9Im05LjI3IDEuNDV2LjI0NWwzLjM4MiAzLjM4M2guMjQ2em0wIDAiIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iLjM5MiIvPjxwYXRoIGQ9Im05LjI3IDEuNDAyYS4wNDcuMDQ3IDAgMCAwIC0uMDQ3LjA0N3YyLjk2NWMwIC4zOTUuMzE2LjcxMS43MS43MTFoMi45NjVjLjAyIDAgLjA0LS4wMTIuMDQzLS4wMzEuMDA4LS4wMTYuMDA0LS4wMzUtLjAwNy0uMDUxbC0zLjYzLTMuNjI5Yy0uMDA3LS4wMDgtLjAyMy0uMDEyLS4wMzQtLjAxMnptLjA0Ni4xNiAzLjQ3IDMuNDdoLTIuODUzYS42MTQuNjE0IDAgMCAxIC0uNjE4LS42MTh6bTAgMCIgZmlsbD0iIzIxMjEyMSIvPjxwYXRoIGQ9Im0zLjMwOSAxLjQwMmEuNzA4LjcwOCAwIDAgMCAtLjcxMS43MDd2MTEuMjQ2YzAgLjM5MS4zMTYuNzA3LjcxLjcwN2g4LjkzYy4zOSAwIC43MDctLjMxNi43MDctLjcwN3YtOC4yNzdjMC0uMDE2LS4wMDQtLjAyNy0uMDExLS4wMzVsLTMuNjMtMy42MjljLS4wMDctLjAwOC0uMDE5LS4wMTItLjAzNC0uMDEyem0wIC4wOTRoNS45NDFsMy42MDIgMy42MDJ2OC4yNTdjMCAuMzQtLjI3NC42MTQtLjYxNC42MTRoLTguOTNhLjYxMy42MTMgMCAwIDEgLS42MTctLjYxNHYtMTEuMjQ1YzAtLjM0LjI3NC0uNjEzLjYxOC0uNjEzem0wIDAiIGZpbGw9IiMyMTIxMjEiLz48ZyBmaWxsPSJ1cmwoI2wpIiBzdHJva2U9IiMyMTIxMjEiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzIj48cGF0aCBkPSJtMTYzLjk4NC0zMS4xNDNoMTY5LjM2YzIuOTk5IDAgNS4zNzQgMi4zNzUgNS4zNzQgNS4zNzV2MTAuNDk5YzAgMy0yLjM3NSA1LjM3NC01LjM3NSA1LjM3NGgtMTY5LjM1OWMtMi44NzQgMC01LjI1LTIuMzc0LTUuMjUtNS4zNzR2LTEwLjVjMC0yLjk5OSAyLjM3Ni01LjM3NCA1LjI1LTUuMzc0em0wIDAiIHRyYW5zZm9ybT0ibWF0cml4KC4wMzEyNSAwIDAgLjAzMTI1IDAgNi43MikiLz48cGF0aCBkPSJtMTYzLjk4NCAxMS4yMjhoMTY5LjM2YzIuOTk5IDAgNS4zNzQgMi4zNzUgNS4zNzQgNS4zNzV2MTAuNDk5YzAgMy0yLjM3NSA1LjM3NC01LjM3NSA1LjM3NGgtMTY5LjM1OWMtMi44NzQgMC01LjI1LTIuMzc0LTUuMjUtNS4zNzR2LTEwLjVjMC0yLjk5OSAyLjM3Ni01LjM3NCA1LjI1LTUuMzc0em0wIDAiIHRyYW5zZm9ybT0ibWF0cml4KC4wMzEyNSAwIDAgLjAzMTI1IDAgNi43MikiLz48cGF0aCBkPSJtMTYzLjk4NCA1My42aDE2OS4zNmMyLjk5OSAwIDUuMzc0IDIuMzc0IDUuMzc0IDUuMjQ5djEwLjYyNGMwIDIuODc1LTIuMzc1IDUuMjUtNS4zNzUgNS4yNWgtMTY5LjM1OWMtMi44NzQgMC01LjI1LTIuMzc1LTUuMjUtNS4yNXYtMTAuNjI0YzAtMi44NzUgMi4zNzYtNS4yNSA1LjI1LTUuMjV6bTAgMCIgdHJhbnNmb3JtPSJtYXRyaXgoLjAzMTI1IDAgMCAuMDMxMjUgMCA2LjcyKSIvPjxwYXRoIGQ9Im0xNjMuOTg0IDk1Ljk3aDE2OS4zNmMyLjk5OSAwIDUuMzc0IDIuMzc1IDUuMzc0IDUuMjV2MTAuNjI0YzAgMi44NzUtMi4zNzUgNS4yNS01LjM3NSA1LjI1aC0xNjkuMzU5Yy0yLjg3NCAwLTUuMjUtMi4zNzUtNS4yNS01LjI1di0xMC42MjRjMC0yLjg3NSAyLjM3Ni01LjI1IDUuMjUtNS4yNXptMCAwIiB0cmFuc2Zvcm09Im1hdHJpeCguMDMxMjUgMCAwIC4wMzEyNSAwIDYuNzIpIi8+PC9nPjwvc3ZnPg=="); | |
| 597 | +} | |
| 598 | + | |
| 599 | +.icon-code, .icon-js, .icon-c, .icon-cpp, .icon-go, .icon-css, .icon-html, .icon-rs, .icon-py, .icon-rb { | |
| 600 | + background-image: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2cHQiIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2cHQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxsaW5lYXJHcmFkaWVudCBpZD0iYSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4yNzUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjguMjY3NSIgeDI9IjguMjY3NSIgeGxpbms6aHJlZj0iI2EiIHkxPSIxNC4wMTY4NzUiIHkyPSIxNC42NzgzNDM3NSIvPjxsaW5lYXJHcmFkaWVudCBpZD0iYyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4zMTQiLz48c3RvcCBvZmZzZXQ9Ii4yMjIiIHN0b3Atb3BhY2l0eT0iLjI3NSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1vcGFjaXR5PSIwIi8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9ImQiIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC0uMDM5MDcgLS4wNzAzMiAwIDI0Ljc0MiAzMS43OTgpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZSIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLjAzOTA3IC4wNzAzMiAwIC05LjE5NyAtMy43NjQpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZiIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLS4wMzkwNyAuMDcwMzIgMCAtOS4xOTcgMzEuNzk4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHI9IjIxLjE2NyIgeGxpbms6aHJlZj0iI2MiLz48cmFkaWFsR3JhZGllbnQgaWQ9ImciIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC4wMzkwNyAtLjA3MDMyIDAgMjQuNzQyIC0zLjc2NCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjciIHhsaW5rOmhyZWY9IiNjIi8+PGxpbmVhckdyYWRpZW50IGlkPSJoIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC4wMDgyNjkwMiAuMDAwMTI3MyAtLjAwMDEyNzMgLjAwODI2OTAyIDAgMCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iOTQwIiB4Mj0iOTQwIiB5MT0iMTY5NS4xMTgiIHkyPSIxNzUuMTE4Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNlMGUwZTAiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNlZWUiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iaSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguMDE0OTUgMCAwIC4wNTQ3IDcuNTIxIC04LjI2MykiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMjQ5LjE4OSIgeDI9IjI0OS4xODkiIHhsaW5rOmhyZWY9IiNhIiB5MT0iMjM2LjUyNCIgeTI9IjI1NC42NjciLz48bGluZWFyR3JhZGllbnQgaWQ9ImoiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzllOWU5ZSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2JkYmRiZCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJrIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjEwLjU4MzM0Mzc1IiB4Mj0iMTAuNTgzMzQzNzUiIHhsaW5rOmhyZWY9IiNqIiB5MT0iMTQuMDE2ODc1IiB5Mj0iMS40NDkxNTYyNSIvPjxsaW5lYXJHcmFkaWVudCBpZD0ibCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIyNDguNjg3OTIiIHgyPSIyNDguNjg3OTIiIHhsaW5rOmhyZWY9IiNqIiB5MT0iMjMzLjQ2OTA4IiB5Mj0iLTE2OC42NzI2NiIvPjxwYXRoIGQ9Im0zLjk2OSAxNC4wMTZoNy42MXYuNjY0aC03LjYxem0wIDAiIGZpbGw9InVybCgjYikiLz48cGF0aCBkPSJtMTEuNTc4IDE0LjAxNmgxLjMydi0uNjZoLTEuMzJ6bTAgMCIgZmlsbD0idXJsKCNkKSIvPjxwYXRoIGQ9Im0zLjk2OSAxNC4wMTZoLTEuMzI0di42NjRoMS4zMjR6bTAgMCIgZmlsbD0idXJsKCNlKSIvPjxwYXRoIGQ9Im0zLjk2OSAxNC4wMTZoLTEuMzI0di0uNjZoMS4zMjR6bTAgMCIgZmlsbD0idXJsKCNmKSIvPjxwYXRoIGQ9Im0xMS41NzggMTQuMDE2aDEuMzJ2LjY2NGgtMS4zMnptMCAwIiBmaWxsPSJ1cmwoI2cpIi8+PHBhdGggZD0ibTkuMjcgMS40NWgtNS45NjFhLjY2LjY2IDAgMCAwIC0uNjY0LjY2djExLjI0NWMwIC4zNjguMjk2LjY2LjY2NC42Nmg4LjkzYy4zNjYgMCAuNjYtLjI5Mi42Ni0uNjZ2LTguMjc3em0wIDAiIGZpbGw9InVybCgjaCkiLz48cGF0aCBkPSJtOS4yNjIgNC41MDhoMy4xNGwuNDk2LjQ5NnYuNjZoLTMuNjM2em0wIDAiIGZpbGw9InVybCgjaSkiLz48cGF0aCBkPSJtMi42NDUgMTMuMDk4di4yNTdjMCAuMzY4LjI5Ni42Ni42NjQuNjZoOC45M2MuMzY2IDAgLjY2LS4yOTIuNjYtLjY2di0uMjU3YS42Ni42NiAwIDAgMSAtLjY2LjY2NGgtOC45M2EuNjY0LjY2NCAwIDAgMSAtLjY2NC0uNjY0em0wIDAiIGZpbGwtb3BhY2l0eT0iLjIzNSIvPjxwYXRoIGQ9Im0zLjMwOSAxLjQ1YS42Ni42NiAwIDAgMCAtLjY2NC42NnYuMjVjMC0uMzY4LjI5Ni0uNjY1LjY2NC0uNjY1aDUuOTZsMy42MyAzLjYzdi0uMjQ3bC0zLjYzMS0zLjYyOHptMCAwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii43ODQiLz48cGF0aCBkPSJtMTIuODk4IDUuMDc4LTMuNjI4LTMuNjI4djIuOTY1YzAgLjM2Ny4yOTYuNjY0LjY2NC42NjR6bTAgMCIgZmlsbD0idXJsKCNrKSIvPjxwYXRoIGQ9Im05LjI3IDQuMjU4di4xNTZjMCAuMzY3LjI5Ni42NjQuNjY0LjY2NGgyLjk2NGwtLjE1Ni0uMTU2aC0yLjgwOGEuNjY0LjY2NCAwIDAgMSAtLjY2NC0uNjY0em0wIDAiIGZpbGwtb3BhY2l0eT0iLjIzNSIvPjxwYXRoIGQ9Im05LjI3IDEuNDV2LjI0NWwzLjM4MiAzLjM4M2guMjQ2em0wIDAiIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iLjM5MiIvPjxwYXRoIGQ9Im05LjI3IDEuNDAyYS4wNDcuMDQ3IDAgMCAwIC0uMDQ3LjA0N3YyLjk2NWMwIC4zOTUuMzE2LjcxMS43MS43MTFoMi45NjVjLjAyIDAgLjA0LS4wMTIuMDQzLS4wMzEuMDA4LS4wMTYuMDA0LS4wMzUtLjAwNy0uMDUxbC0zLjYzLTMuNjI5Yy0uMDA3LS4wMDgtLjAyMy0uMDEyLS4wMzQtLjAxMnptLjA0Ni4xNiAzLjQ3IDMuNDdoLTIuODUzYS42MTQuNjE0IDAgMCAxIC0uNjE4LS42MTh6bTAgMCIgZmlsbD0iIzIxMjEyMSIvPjxwYXRoIGQ9Im0zLjMwOSAxLjQwMmEuNzA4LjcwOCAwIDAgMCAtLjcxMS43MDd2MTEuMjQ2YzAgLjM5MS4zMTYuNzA3LjcxLjcwN2g4LjkzYy4zOSAwIC43MDctLjMxNi43MDctLjcwN3YtOC4yNzdjMC0uMDE2LS4wMDQtLjAyNy0uMDExLS4wMzVsLTMuNjMtMy42MjljLS4wMDctLjAwOC0uMDE5LS4wMTItLjAzNC0uMDEyem0wIC4wOTRoNS45NDFsMy42MDIgMy42MDJ2OC4yNTdjMCAuMzQtLjI3NC42MTQtLjYxNC42MTRoLTguOTNhLjYxMy42MTMgMCAwIDEgLS42MTctLjYxNHYtMTEuMjQ1YzAtLjM0LjI3NC0uNjEzLjYxOC0uNjEzem0wIDAiIGZpbGw9IiMyMTIxMjEiLz48cGF0aCBkPSJtMjA2LjM1NS0zMS4xNDNjLTIuODc0IDAtNS4yNSAyLjM3NS01LjI1IDUuMzc1djI2LjM3MmgtMjEuMjQ3Yy0yLjg3NSAwLTUuMjUgMi4zNzUtNS4yNSA1LjM3NXYxMC42MjRjMCAyLjg3NSAyLjM3NSA1LjI1IDUuMjUgNS4yNWgyMS4yNDh2NDIuMzdoLTIxLjI0OGMtMi44NzUgMC01LjI1IDIuMjUtNS4yNSA1LjI1djEwLjYyNGMwIDIuODc1IDIuMzc1IDUuMjUgNS4yNSA1LjI1aDIxLjI0OHYyNi40OTdjMCAyLjg3NSAyLjM3NSA1LjI1IDUuMjUgNS4yNWgxMC42MjNjMi44NzUgMCA1LjI1LTIuMzc1IDUuMjUtNS4yNXYtMjYuNDk4aDUyLjk5NXYyNi40OThjMCAyLjg3NSAyLjI1IDUuMjUgNS4yNSA1LjI1aDEwLjYyM2MyLjg3NSAwIDUuMjUtMi4zNzUgNS4yNS01LjI1di0yNi40OThoMjEuMTIzYzMgMCA1LjM3NC0yLjM3NCA1LjM3NC01LjI1di0xMC42MjJjMC0zLTIuMzc1LTUuMjUtNS4zNzQtNS4yNWgtMjEuMTIzdi00Mi4zN2gyMS4xMjNjMyAwIDUuMzc0LTIuMzc1IDUuMzc0LTUuMjV2LTEwLjYyNWMwLTMtMi4zNzUtNS4zNzUtNS4zNzQtNS4zNzVoLTIxLjEyM3YtMjYuMzcyYzAtMy0yLjM3NS01LjM3NS01LjI1LTUuMzc1aC0xMC42MjRjLTMgMC01LjI1IDIuMzc1LTUuMjUgNS4zNzV2MjYuMzcyaC01Mi45OTN2LTI2LjM3MmMwLTMtMi4zNzUtNS4zNzUtNS4yNS01LjM3NXptMTUuODc0IDUyLjk5NWg1Mi45OTV2NDIuMzcxaC01Mi45OTV6bTAgMCIgZmlsbD0idXJsKCNsKSIgc3Ryb2tlPSIjMjEyMTIxIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAzMTI1IDAgMCAuMDMxMjUgMCA2LjcyKSIvPjwvc3ZnPg=="); | |
| 601 | +} | |
| 602 | + | |
| 603 | +.icon-doc, .icon-docx, .icon-odt { | |
| 604 | + background-image: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2cHQiIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2cHQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxsaW5lYXJHcmFkaWVudCBpZD0iYSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4yNzUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjguMjY3NSIgeDI9IjguMjY3NSIgeGxpbms6aHJlZj0iI2EiIHkxPSIxNC4wMTY4NzUiIHkyPSIxNC42NzgzNDM3NSIvPjxsaW5lYXJHcmFkaWVudCBpZD0iYyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4zMTQiLz48c3RvcCBvZmZzZXQ9Ii4yMjIiIHN0b3Atb3BhY2l0eT0iLjI3NSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1vcGFjaXR5PSIwIi8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9ImQiIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC0uMDM5MDcgLS4wNzAzMiAwIDI0Ljc0MiAzMS43OTgpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZSIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLjAzOTA3IC4wNzAzMiAwIC05LjE5NyAtMy43NjQpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZiIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLS4wMzkwNyAuMDcwMzIgMCAtOS4xOTcgMzEuNzk4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHI9IjIxLjE2NyIgeGxpbms6aHJlZj0iI2MiLz48cmFkaWFsR3JhZGllbnQgaWQ9ImciIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC4wMzkwNyAtLjA3MDMyIDAgMjQuNzQyIC0zLjc2NCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjciIHhsaW5rOmhyZWY9IiNjIi8+PGxpbmVhckdyYWRpZW50IGlkPSJoIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC4wMDgyNjkwMiAuMDAwMTI3MyAtLjAwMDEyNzMgLjAwODI2OTAyIDAgMCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iOTQwIiB4Mj0iOTQwIiB5MT0iMTY5NS4xMTgiIHkyPSIxNzUuMTE4Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiM5ZmE4ZGEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNjNWNhZTkiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iaSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguMDE0OTUgMCAwIC4wNTQ3IDcuNTIxIC04LjE4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIyNDkuMTg5IiB4Mj0iMjQ5LjE4OSIgeGxpbms6aHJlZj0iI2EiIHkxPSIyMzMuNSIgeTI9IjI1NC42NjciLz48ZmlsdGVyIGlkPSJqIiBoZWlnaHQ9IjEwMCUiIHdpZHRoPSIxMDAlIiB4PSIwJSIgeT0iMCUiPjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VHcmFwaGljIiB2YWx1ZXM9IjAgMCAwIDAgMSAwIDAgMCAwIDEgMCAwIDAgMCAxIDAgMCAwIDEgMCIvPjwvZmlsdGVyPjxtYXNrIGlkPSJrIj48cGF0aCBkPSJtMCAwaDE2djE2aC0xNnoiIGZpbGwtb3BhY2l0eT0iLjk4OCIgZmlsdGVyPSJ1cmwoI2opIi8+PC9tYXNrPjxsaW5lYXJHcmFkaWVudCBpZD0ibCI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjNWM2YmMwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNzk4NmNiIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9Im0iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iOTYwIiB4Mj0iOTYwIiB4bGluazpocmVmPSIjbCIgeTE9IjE2OTUuMTE4IiB5Mj0iMTc1LjExOCIvPjxjbGlwUGF0aCBpZD0ibiI+PHBhdGggZD0ibTAgMGgxNnYxNmgtMTZ6Ii8+PC9jbGlwUGF0aD48bGluZWFyR3JhZGllbnQgaWQ9Im8iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMTAuNTgzMzQzNzUiIHgyPSIxMC41ODMzNDM3NSIgeGxpbms6aHJlZj0iI2wiIHkxPSIxNC4wMTY4NzUiIHkyPSIxLjQ0OTE1NjI1Ii8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmg3LjYxdi42NjRoLTcuNjF6bTAgMCIgZmlsbD0idXJsKCNiKSIvPjxwYXRoIGQ9Im0xMS41NzggMTQuMDE2aDEuMzJ2LS42NmgtMS4zMnptMCAwIiBmaWxsPSJ1cmwoI2QpIi8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmgtMS4zMjR2LjY2NGgxLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2UpIi8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmgtMS4zMjR2LS42NmgxLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2YpIi8+PHBhdGggZD0ibTExLjU3OCAxNC4wMTZoMS4zMnYuNjY0aC0xLjMyem0wIDAiIGZpbGw9InVybCgjZykiLz48cGF0aCBkPSJtOS4yNyAxLjQ1aC01Ljk2MWEuNjYuNjYgMCAwIDAgLS42NjQuNjZ2MTEuMjQ1YzAgLjM2OC4yOTYuNjYuNjY0LjY2aDguOTNjLjM2NiAwIC42Ni0uMjkyLjY2LS42NnYtOC4yNzd6bTAgMCIgZmlsbD0idXJsKCNoKSIvPjxwYXRoIGQ9Im05LjI2MiA0LjU5aDMuMTRsLjQ5Ni40OTZ2LjY2aC0zLjYzNnptMCAwIiBmaWxsPSJ1cmwoI2kpIi8+PHBhdGggZD0ibTIuNjQ1IDEzLjA5OHYuMjU3YzAgLjM2OC4yOTYuNjYuNjY0LjY2aDguOTNjLjM2NiAwIC42Ni0uMjkyLjY2LS42NnYtLjI1N2EuNjYuNjYgMCAwIDEgLS42Ni42NjRoLTguOTNhLjY2NC42NjQgMCAwIDEgLS42NjQtLjY2NHptMCAwIiBmaWxsLW9wYWNpdHk9Ii4yMzUiLz48ZyBjbGlwLXBhdGg9InVybCgjbikiIG1hc2s9InVybCgjaykiPjxwYXRoIGQ9Im02MTkuNzgzIDY5NC44OTRjLTEwLjg2NSAwLTE5Ljg0IDguOTc2LTE5Ljg0IDIwLjMxM3YzOS42ODJjMCAxMS4zMzcgOC45NzUgMjAuMzEzIDE5Ljg0IDIwLjMxM2g2NDAuMDk3YzExLjMzNyAwIDIwLjMxMy04Ljk3NiAyMC4zMTMtMjAuMzEzdi0zOS42ODJjMC0xMS4zMzctOC45NzYtMjAuMzEzLTIwLjMxMy0yMC4zMTN6bTAgMTYwLjE0M2MtMTAuODY1IDAtMTkuODQgOC45NzUtMTkuODQgMjAuMzEzdjM5LjY4YzAgMTEuMzM4IDguOTc1IDIwLjMxNCAxOS44NCAyMC4zMTRoNjQwLjA5N2MxMS4zMzcgMCAyMC4zMTMtOC45NzYgMjAuMzEzLTIwLjMxM3YtMzkuNjgxYzAtMTEuMzM4LTguOTc2LTIwLjMxMy0yMC4zMTMtMjAuMzEzem0wIDE2MC4xNDJjLTEwLjg2NSAwLTE5Ljg0IDguOTc1LTE5Ljg0IDE5Ljg0djQwLjE1NGMwIDEwLjg2NSA4Ljk3NSAxOS44NCAxOS44NCAxOS44NGg2NDAuMDk3YzExLjMzNyAwIDIwLjMxMy04Ljk3NSAyMC4zMTMtMTkuODR2LTQwLjE1NGMwLTEwLjg2NS04Ljk3Ni0xOS44NC0yMC4zMTMtMTkuODR6bTAgMTYwLjE0MmMtMTAuODY1IDAtMTkuODQgOC41MDMtMTkuODQgMTkuODR2NDAuMTU0YzAgMTAuODY1IDguOTc1IDE5Ljg0IDE5Ljg0IDE5Ljg0aDY0MC4wOTdjMTEuMzM3IDAgMjAuMzEzLTguOTc1IDIwLjMxMy0xOS44NHYtNDAuMTUzYzAtMTEuMzM4LTguOTc2LTE5Ljg0MS0yMC4zMTMtMTkuODQxem0wIDAiIGZpbGw9InVybCgjbSkiIHN0cm9rZT0iIzIxMjEyMSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjExLjMzOSIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwODI2OTAyIC4wMDAxMjczIC0uMDAwMTI3MyAuMDA4MjY5MDIgMCAwKSIvPjwvZz48cGF0aCBkPSJtMy4zMDkgMS40NWEuNjYuNjYgMCAwIDAgLS42NjQuNjZ2LjI1YzAtLjM2OC4yOTYtLjY2NS42NjQtLjY2NWg1Ljk2bDMuNjMgMy42M3YtLjI0N2wtMy42MzEtMy42Mjh6bTAgMCIgZmlsbD0iI2Y2ZjZmNiIgZmlsbC1vcGFjaXR5PSIuNTg4Ii8+PHBhdGggZD0ibTEyLjg5OCA1LjA3OC0zLjYyOC0zLjYyOHYyLjk2NWMwIC4zNjcuMjk2LjY2NC42NjQuNjY0em0wIDAiIGZpbGw9InVybCgjbykiLz48cGF0aCBkPSJtOS4yNyA0LjI1OHYuMTU2YzAgLjM2Ny4yOTYuNjY0LjY2NC42NjRoMi45NjRsLS4xNTYtLjE1NmgtMi44MDhhLjY2NC42NjQgMCAwIDEgLS42NjQtLjY2NHptMCAwIiBmaWxsLW9wYWNpdHk9Ii4yMzUiLz48cGF0aCBkPSJtOS4yNyAxLjQ1di4yNDVsMy4zODIgMy4zODNoLjI0NnptMCAwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4zOTIiLz48ZyBmaWxsPSIjMjEyMTIxIj48cGF0aCBkPSJtOS4yNyAxLjQwMmEuMDQ3LjA0NyAwIDAgMCAtLjA0Ny4wNDd2Mi45NjVjMCAuMzk1LjMxNi43MTEuNzEuNzExaDIuOTY1Yy4wMiAwIC4wNC0uMDEyLjA0My0uMDMxLjAwOC0uMDE2LjAwNC0uMDM1LS4wMDctLjA1MWwtMy42My0zLjYyOWMtLjAwNy0uMDA4LS4wMjMtLjAxMi0uMDM0LS4wMTJ6bS4wNDYuMTYgMy40NyAzLjQ3aC0yLjg1M2EuNjE0LjYxNCAwIDAgMSAtLjYxOC0uNjE4em0wIDAiLz48cGF0aCBkPSJtMy4zMDkgMS40MDJhLjcwOC43MDggMCAwIDAgLS43MTEuNzA3djExLjI0NmMwIC4zOTEuMzE2LjcwNy43MS43MDdoOC45M2MuMzkgMCAuNzA3LS4zMTYuNzA3LS43MDd2LTguMjc3YzAtLjAxNi0uMDA0LS4wMjctLjAxMS0uMDM1bC0zLjYzLTMuNjI5Yy0uMDA3LS4wMDgtLjAxOS0uMDEyLS4wMzQtLjAxMnptMCAuMDk0aDUuOTQxbDMuNjAyIDMuNjAydjguMjU3YzAgLjM0LS4yNzQuNjE0LS42MTQuNjE0aC04LjkzYS42MTMuNjEzIDAgMCAxIC0uNjE3LS42MTR2LTExLjI0NWMwLS4zNC4yNzQtLjYxMy42MTgtLjYxM3ptMCAwIi8+PC9nPjwvc3ZnPg=="); | |
| 605 | +} | |
| 606 | + | |
| 607 | +.icon-epub, .icon-mobi { | |
| 608 | + background-image: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2cHQiIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2cHQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxsaW5lYXJHcmFkaWVudCBpZD0iYSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSI3LjkzNzUiIHgyPSI3LjkzNzUiIHkxPSIxNC4wMTY4NzUiIHkyPSIxNC42NzgzNDM3NSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4yNzUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3Atb3BhY2l0eT0iLjMxNCIvPjxzdG9wIG9mZnNldD0iLjIyMiIgc3RvcC1vcGFjaXR5PSIuMjc1Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9IjAiLz48L2xpbmVhckdyYWRpZW50PjxyYWRpYWxHcmFkaWVudCBpZD0iYyIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLS4wMzkwNyAtLjA3MDMyIDAgMjUuMDczIDMxLjc5OCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjciIHhsaW5rOmhyZWY9IiNiIi8+PHJhZGlhbEdyYWRpZW50IGlkPSJkIiBjeD0iNDUwLjkwOSIgY3k9IjE4OS41NzkiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMCAuMDM5MDcgLjA3MDMyIDAgLTkuMTk3IC0zLjc2NCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjciIHhsaW5rOmhyZWY9IiNiIi8+PHJhZGlhbEdyYWRpZW50IGlkPSJlIiBjeD0iNDUwLjkwOSIgY3k9IjE4OS41NzkiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMCAtLjAzOTA3IC4wNzAzMiAwIC05LjE5NyAzMS43OTgpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYiIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZiIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLjAzOTA3IC0uMDcwMzIgMCAyNS4wNzMgLTMuNzY0KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHI9IjIxLjE2NyIgeGxpbms6aHJlZj0iI2IiLz48bGluZWFyR3JhZGllbnQgaWQ9ImciIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLjAzNzY3IDAgMCAuMTA4NyAtMi45MjcgMTYuODIpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjMwNi45MTciIHgyPSIzMDUuOTU1IiB5MT0iLTI1Ljc5MiIgeTI9Ii0xNDEuNDE1Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiM5MGNhZjkiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNiYmRlZmIiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iaCI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjNDJhNWY1Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNjRiNWY2Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImkiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoNC41NTYxNCAwIDAgMTMuMTQ2MTggLTM1My45NzMgMjAzNC4xOCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMTY1LjQ4NSIgeDI9IjE2NS40ODUiIHhsaW5rOmhyZWY9IiNoIiB5MT0iLTI1Ljc5MiIgeTI9Ii0xNDEuNDE1Ii8+PGxpbmVhckdyYWRpZW50IGlkPSJqIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ijc2MCIgeDI9Ijc2MCIgeGxpbms6aHJlZj0iI2giIHkxPSIxNjk1LjExNyIgeTI9IjE3NS4xMTciLz48cGF0aCBkPSJtMy45NjkgMTQuMDE2aDcuOTM3di42NjRoLTcuOTM2em0wIDAiIGZpbGw9InVybCgjYSkiLz48cGF0aCBkPSJtMTEuOTA2IDE0LjAxNmgxLjMyNHYtLjY2aC0xLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2MpIi8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmgtMS4zMjR2LjY2NGgxLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2QpIi8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmgtMS4zMjR2LS42NmgxLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2UpIi8+PHBhdGggZD0ibTExLjkwNiAxNC4wMTZoMS4zMjR2LjY2NGgtMS4zMjR6bTAgMCIgZmlsbD0idXJsKCNmKSIvPjxwYXRoIGQ9Im0zLjMwOSAxLjQ1aDkuMjYxYy4zNjQgMCAuNjYuMjk2LjY2LjY2djExLjI0NWEuNjYuNjYgMCAwIDEgLS42Ni42NmgtOS4yNmEuNjYuNjYgMCAwIDEgLS42NjQtLjY2di0xMS4yNDVjMC0uMzYzLjI5Ni0uNjYuNjY0LS42NnptMCAwIiBmaWxsPSJ1cmwoI2cpIi8+PHBhdGggZD0ibTQwMC4xMiAxNzUuMjU5Yy00NC40MDYgMC04MC4zMDggMzUuNDMtODAuMzA4IDc5LjgzNXYxMzYwLjAyNmMwIDQ0LjQwNiAzNS45MDIgNzkuODM1IDgwLjMwNyA3OS44MzVoMTE5Ljk4OXYtMTUxOS42OTV6bTAgMCIgZmlsbD0idXJsKCNpKSIgc3Ryb2tlPSIjMjEyMTIxIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS13aWR0aD0iMTEuMzM5IiB0cmFuc2Zvcm09Im1hdHJpeCguMDA4MjY5MDIgLjAwMDEyNzMgLS4wMDAxMjczIC4wMDgyNjkwMiAwIDApIi8+PHBhdGggZD0ibTMuMzA5IDEzLjk4YS42NjQuNjY0IDAgMCAxIC0uNjY0LS42NjR2LS4wODJjMCAuMzY4LjI5Ni42NjQuNjY0LjY2NGg5LjI2MWMuMzY0IDAgLjY2LS4yOTYuNjYtLjY2NHYuMDgyYS42NjMuNjYzIDAgMCAxIC0uNjYuNjY0em0wIDAiIGZpbGwtb3BhY2l0eT0iLjE5NiIvPjxwYXRoIGQ9Im02NzkuNzc4IDE3NS4yNTl2OTU5LjkwOGM1OS45OTQtMTYwLjE0MiAxMDAuMTQ4LTE2MC4xNDIgMTYwLjE0MiAwdi05NTkuOTA3em0wIDAiIGZpbGw9InVybCgjaikiIHN0cm9rZT0iIzIxMjEyMSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjExLjMzOSIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwODI2OTAyIC4wMDAxMjczIC0uMDAwMTI3MyAuMDA4MjY5MDIgMCAwKSIvPjxwYXRoIGQ9Im0zLjMwOSAxLjQ4NGEuNjY0LjY2NCAwIDAgMCAtLjY2NC42NjR2LjA4MmMwLS4zNjcuMjk2LS42NjQuNjY0LS42NjRoOS4yNjFjLjM2NCAwIC42Ni4yOTcuNjYuNjY0di0uMDgyYS42NjMuNjYzIDAgMCAwIC0uNjYtLjY2NHptMCAwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4zOTIiLz48cGF0aCBkPSJtMy4zMDkgMS40MDJhLjcwOC43MDggMCAwIDAgLS43MTEuNzA3djExLjI0NmMwIC4zOTEuMzE2LjcwNy43MS43MDdoOS4yNjJjLjM5IDAgLjcwNy0uMzE2LjcwNy0uNzA3di0xMS4yNDVhLjcwNy43MDcgMCAwIDAgLS43MDctLjcwN3ptMCAuMDk0aDkuMjYxYy4zNCAwIC42MTQuMjc0LjYxNC42MTN2MTEuMjQ2YzAgLjM0LS4yNzQuNjE0LS42MTQuNjE0aC05LjI2YS42MTMuNjEzIDAgMCAxIC0uNjE4LS42MTR2LTExLjI0NWMwLS4zNC4yNzQtLjYxMy42MTgtLjYxM3ptMCAwIiBmaWxsPSIjMjEyMTIxIi8+PC9zdmc+") | |
| 609 | +} | |
| 610 | + | |
| 611 | +.icon-gif, .icon-png, .icon-jpg, .icon-jpeg, .icon-gif { | |
| 612 | + background-image: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2cHQiIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2cHQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxsaW5lYXJHcmFkaWVudCBpZD0iYSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4yNzUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjguMjY3NSIgeDI9IjguMjY3NSIgeGxpbms6aHJlZj0iI2EiIHkxPSIxNC4wMTY4NzUiIHkyPSIxNC42NzgzNDM3NSIvPjxsaW5lYXJHcmFkaWVudCBpZD0iYyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4zMTQiLz48c3RvcCBvZmZzZXQ9Ii4yMjIiIHN0b3Atb3BhY2l0eT0iLjI3NSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1vcGFjaXR5PSIwIi8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9ImQiIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC0uMDM5MDcgLS4wNzAzMiAwIDI0Ljc0MiAzMS43OTgpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZSIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLjAzOTA3IC4wNzAzMiAwIC05LjE5NyAtMy43NjQpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZiIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLS4wMzkwNyAuMDcwMzIgMCAtOS4xOTcgMzEuNzk4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHI9IjIxLjE2NyIgeGxpbms6aHJlZj0iI2MiLz48cmFkaWFsR3JhZGllbnQgaWQ9ImciIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC4wMzkwNyAtLjA3MDMyIDAgMjQuNzQyIC0zLjc2NCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjciIHhsaW5rOmhyZWY9IiNjIi8+PGxpbmVhckdyYWRpZW50IGlkPSJoIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC4wMDgyNjkwMiAuMDAwMTI3MyAtLjAwMDEyNzMgLjAwODI2OTAyIDAgMCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iOTQwIiB4Mj0iOTQwIiB5MT0iMTY5NS4xMTgiIHkyPSIxNzUuMTE4Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNhNWQ2YTciLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNjOGU2YzkiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iaSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguMDE0OTUgMCAwIC4wNTQ3IDcuNTIxIC04LjE4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIyNDkuMTg5IiB4Mj0iMjQ5LjE4OSIgeGxpbms6aHJlZj0iI2EiIHkxPSIyMzMuNSIgeTI9IjI1NC42NjciLz48bGluZWFyR3JhZGllbnQgaWQ9ImoiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzY2YmI2YSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzgxYzc4NCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJrIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjEwLjU4MzM0Mzc1IiB4Mj0iMTAuNTgzMzQzNzUiIHhsaW5rOmhyZWY9IiNqIiB5MT0iMTQuMDE2ODc1IiB5Mj0iMS40NDkxNTYyNSIvPjxsaW5lYXJHcmFkaWVudCBpZD0ibCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSI5NDAiIHgyPSI5NDAiIHhsaW5rOmhyZWY9IiNqIiB5MT0iMTY5NS4xMTgiIHkyPSIxNzUuMTE4Ii8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmg3LjYxdi42NjRoLTcuNjF6bTAgMCIgZmlsbD0idXJsKCNiKSIvPjxwYXRoIGQ9Im0xMS41NzggMTQuMDE2aDEuMzJ2LS42NmgtMS4zMnptMCAwIiBmaWxsPSJ1cmwoI2QpIi8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmgtMS4zMjR2LjY2NGgxLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2UpIi8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmgtMS4zMjR2LS42NmgxLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2YpIi8+PHBhdGggZD0ibTExLjU3OCAxNC4wMTZoMS4zMnYuNjY0aC0xLjMyem0wIDAiIGZpbGw9InVybCgjZykiLz48cGF0aCBkPSJtOS4yNyAxLjQ1aC01Ljk2MWEuNjYuNjYgMCAwIDAgLS42NjQuNjZ2MTEuMjQ1YzAgLjM2OC4yOTYuNjYuNjY0LjY2aDguOTNjLjM2NiAwIC42Ni0uMjkyLjY2LS42NnYtOC4yNzd6bTAgMCIgZmlsbD0idXJsKCNoKSIvPjxwYXRoIGQ9Im05LjI2MiA0LjU5aDMuMTRsLjQ5Ni40OTZ2LjY2aC0zLjYzNnptMCAwIiBmaWxsPSJ1cmwoI2kpIi8+PHBhdGggZD0ibTIuNjQ1IDEzLjA5OHYuMjU3YzAgLjM2OC4yOTYuNjYuNjY0LjY2aDguOTNjLjM2NiAwIC42Ni0uMjkyLjY2LS42NnYtLjI1N2EuNjYuNjYgMCAwIDEgLS42Ni42NjRoLTguOTNhLjY2NC42NjQgMCAwIDEgLS42NjQtLjY2NHptMCAwIiBmaWxsLW9wYWNpdHk9Ii4yMzUiLz48cGF0aCBkPSJtMy4zMDkgMS40NWEuNjYuNjYgMCAwIDAgLS42NjQuNjZ2LjI1YzAtLjM2OC4yOTYtLjY2NS42NjQtLjY2NWg1Ljk2bDMuNjMgMy42M3YtLjI0N2wtMy42MzEtMy42Mjh6bTAgMCIgZmlsbD0iI2Y2ZjZmNiIgZmlsbC1vcGFjaXR5PSIuNTg4Ii8+PHBhdGggZD0ibTEyLjg5OCA1LjA3OC0zLjYyOC0zLjYyOHYyLjk2NWMwIC4zNjcuMjk2LjY2NC42NjQuNjY0em0wIDAiIGZpbGw9InVybCgjaykiLz48cGF0aCBkPSJtOS4yNyA0LjI1OHYuMTU2YzAgLjM2Ny4yOTYuNjY0LjY2NC42NjRoMi45NjRsLS4xNTYtLjE1NmgtMi44MDhhLjY2NC42NjQgMCAwIDEgLS42NjQtLjY2NHptMCAwIiBmaWxsLW9wYWNpdHk9Ii4yMzUiLz48cGF0aCBkPSJtOS4yNyAxLjQ1di4yNDVsMy4zODIgMy4zODNoLjI0NnptMCAwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4zOTIiLz48cGF0aCBkPSJtOS4yNyAxLjQwMmEuMDQ3LjA0NyAwIDAgMCAtLjA0Ny4wNDd2Mi45NjVjMCAuMzk1LjMxNi43MTEuNzEuNzExaDIuOTY1Yy4wMiAwIC4wNC0uMDEyLjA0My0uMDMxLjAwOC0uMDE2LjAwNC0uMDM1LS4wMDctLjA1MWwtMy42My0zLjYyOWMtLjAwNy0uMDA4LS4wMjMtLjAxMi0uMDM0LS4wMTJ6bS4wNDYuMTYgMy40NyAzLjQ3aC0yLjg1M2EuNjE0LjYxNCAwIDAgMSAtLjYxOC0uNjE4em0wIDAiIGZpbGw9IiMyMTIxMjEiLz48cGF0aCBkPSJtMy4zMDkgMS40MDJhLjcwOC43MDggMCAwIDAgLS43MTEuNzA3djExLjI0NmMwIC4zOTEuMzE2LjcwNy43MS43MDdoOC45M2MuMzkgMCAuNzA3LS4zMTYuNzA3LS43MDd2LTguMjc3YzAtLjAxNi0uMDA0LS4wMjctLjAxMS0uMDM1bC0zLjYzLTMuNjI5Yy0uMDA3LS4wMDgtLjAxOS0uMDEyLS4wMzQtLjAxMnptMCAuMDk0aDUuOTQxbDMuNjAyIDMuNjAydjguMjU3YzAgLjM0LS4yNzQuNjE0LS42MTQuNjE0aC04LjkzYS42MTMuNjEzIDAgMCAxIC0uNjE3LS42MTR2LTExLjI0NWMwLS4zNC4yNzQtLjYxMy42MTgtLjYxM3ptMCAwIiBmaWxsPSIjMjEyMTIxIi8+PHBhdGggZD0ibTYxOS43ODMgNjk0Ljg5NGMtMTAuODY1IDAtMTkuODQgOC45NzYtMTkuODQgMjAuMzEzdjUyMC4xMDhjMCAxMC44NjUgOC45NzUgMTkuODQgMTkuODQgMTkuODRoNjQwLjA5N2MxMS4zMzcgMCAyMC4zMTMtOC45NzUgMjAuMzEzLTE5Ljg0di01MjAuMTA4YzAtMTEuMzM3LTguOTc2LTIwLjMxMy0yMC4zMTMtMjAuMzEzem04MC4zMDggODAuMzA4aDQ3OS45NTRjMTAuODY1IDAgMTkuODQgOC45NzUgMTkuODQgMTkuODR2MzU5Ljk2NmMwIDExLjMzNy0xMC44NjUgMjYuNDU0LTE5Ljg0IDIwLjMxMy0zNy4zMi0yNS45ODItNTkuOTk0LTU5Ljk5NC0xMDAuMTQ4LTU5Ljk5NC00Ni4yOTUgMC05MC4yMjggMzkuNjgtMTM5LjgzIDM5LjY4LTQwLjE1MyAwLTU5Ljk5NC0xMTkuOTg4LTExOS45ODgtMTE5Ljk4OHMtNjUuMTkgMzQuMDEzLTExOS45ODggMTQwLjMwMmMtNS4xOTcgOS40NDgtMjAuMzEzLTguOTc2LTIwLjMxMy0yMC4zMTN2LTM1OS45NjZjMC0xMC44NjUgOC45NzUtMTkuODQgMjAuMzEzLTE5Ljg0em0zNTAuMDQ1IDU5Ljk5NGMtMzguNzM2IDAtNjkuOTE1IDMxLjE3OC02OS45MTUgNjkuOTE0IDAgMzguNzM3IDMxLjE3OSA2OS45MTUgNjkuOTE1IDY5LjkxNXM2OS45MTUtMzEuMTc4IDY5LjkxNS02OS45MTVjMC0zOC43MzYtMzEuMTc5LTY5LjkxNC02OS45MTUtNjkuOTE0em0wIDAiIGZpbGw9InVybCgjbCkiIHN0cm9rZT0iIzIxMjEyMSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjExLjMzOSIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwODI2OTAyIC4wMDAxMjczIC0uMDAwMTI3MyAuMDA4MjY5MDIgMCAwKSIvPjwvc3ZnPg=="); | |
| 613 | +} | |
| 614 | + | |
| 615 | +.icon-mp3, .icon-wav, .icon-flac, .icon-ogg { | |
| 616 | + background-image: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2cHQiIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2cHQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxsaW5lYXJHcmFkaWVudCBpZD0iYSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4yNzUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjguMjY3NSIgeDI9IjguMjY3NSIgeGxpbms6aHJlZj0iI2EiIHkxPSIxNC4wMTY4NzUiIHkyPSIxNC42NzgzNDM3NSIvPjxsaW5lYXJHcmFkaWVudCBpZD0iYyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4zMTQiLz48c3RvcCBvZmZzZXQ9Ii4yMjIiIHN0b3Atb3BhY2l0eT0iLjI3NSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1vcGFjaXR5PSIwIi8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9ImQiIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC0uMDM5MDcgLS4wNzAzMiAwIDI0Ljc0MiAzMS43OTgpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZSIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLjAzOTA3IC4wNzAzMiAwIC05LjE5NyAtMy43NjQpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZiIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLS4wMzkwNyAuMDcwMzIgMCAtOS4xOTcgMzEuNzk4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHI9IjIxLjE2NyIgeGxpbms6aHJlZj0iI2MiLz48cmFkaWFsR3JhZGllbnQgaWQ9ImciIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC4wMzkwNyAtLjA3MDMyIDAgMjQuNzQyIC0zLjc2NCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjciIHhsaW5rOmhyZWY9IiNjIi8+PGxpbmVhckdyYWRpZW50IGlkPSJoIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC4wMDgyNjkwMiAuMDAwMTI3MyAtLjAwMDEyNzMgLjAwODI2OTAyIDAgMCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iOTQwIiB4Mj0iOTQwIiB5MT0iMTY5NS4xMTgiIHkyPSIxNzUuMTE4Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNmZmNjODAiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmUwYjIiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iaSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguMDE0OTUgMCAwIC4wNTQ3IDcuNTIxIC04LjI2MykiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMjQ5LjE4OSIgeDI9IjI0OS4xODkiIHhsaW5rOmhyZWY9IiNhIiB5MT0iMjMzLjUiIHkyPSIyNTQuNjY3Ii8+PGxpbmVhckdyYWRpZW50IGlkPSJqIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNmZmE3MjYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmI3NGQiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iayIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxMC41ODMzNDM3NSIgeDI9IjEwLjU4MzM0Mzc1IiB4bGluazpocmVmPSIjaiIgeTE9IjE0LjAxNjg3NSIgeTI9IjEuNDQ5MTU2MjUiLz48bGluZWFyR3JhZGllbnQgaWQ9ImwiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iNTIuOTE1MzAzNDIiIHgyPSI1Mi45MTUzMDM0MiIgeGxpbms6aHJlZj0iI2oiIHkxPSIyMzMuNDk0MzIwNDQiIHkyPSItMTY4LjY2NzI3OTU2Ii8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmg3LjYxdi42NjRoLTcuNjF6bTAgMCIgZmlsbD0idXJsKCNiKSIvPjxwYXRoIGQ9Im0xMS41NzggMTQuMDE2aDEuMzJ2LS42NmgtMS4zMnptMCAwIiBmaWxsPSJ1cmwoI2QpIi8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmgtMS4zMjR2LjY2NGgxLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2UpIi8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmgtMS4zMjR2LS42NmgxLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2YpIi8+PHBhdGggZD0ibTExLjU3OCAxNC4wMTZoMS4zMnYuNjY0aC0xLjMyem0wIDAiIGZpbGw9InVybCgjZykiLz48cGF0aCBkPSJtOS4yNyAxLjQ1aC01Ljk2MWEuNjYuNjYgMCAwIDAgLS42NjQuNjZ2MTEuMjQ1YzAgLjM2OC4yOTYuNjYuNjY0LjY2aDguOTNjLjM2NiAwIC42Ni0uMjkyLjY2LS42NnYtOC4yNzd6bTAgMCIgZmlsbD0idXJsKCNoKSIvPjxwYXRoIGQ9Im0yLjY0NSAxMy4wOTh2LjI1N2MwIC4zNjguMjk2LjY2LjY2NC42Nmg4LjkzYy4zNjYgMCAuNjYtLjI5Mi42Ni0uNjZ2LS4yNTdhLjY2LjY2IDAgMCAxIC0uNjYuNjY0aC04LjkzYS42NjQuNjY0IDAgMCAxIC0uNjY0LS42NjR6bTAgMCIgZmlsbC1vcGFjaXR5PSIuMjM1Ii8+PHBhdGggZD0ibTkuMjYyIDQuNTA4aDMuMTRsLjQ5Ni40OTZ2LjY2aC0zLjYzNnptMCAwIiBmaWxsPSJ1cmwoI2kpIi8+PHBhdGggZD0ibTMuMzA5IDEuNDVhLjY2LjY2IDAgMCAwIC0uNjY0LjY2di4yNWMwLS4zNjguMjk2LS42NjUuNjY0LS42NjVoNS45NmwzLjYzIDMuNjN2LS4yNDdsLTMuNjMxLTMuNjI4em0wIDAiIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iLjM5MiIvPjxwYXRoIGQ9Im0xMi44OTggNS4wNzgtMy42MjgtMy42Mjh2Mi45NjVjMCAuMzY3LjI5Ni42NjQuNjY0LjY2NHptMCAwIiBmaWxsPSJ1cmwoI2spIi8+PHBhdGggZD0ibTkuMjcgNC4yNTh2LjE1NmMwIC4zNjcuMjk2LjY2NC42NjQuNjY0aDIuOTY0bC0uMTU2LS4xNTZoLTIuODA4YS42NjQuNjY0IDAgMCAxIC0uNjY0LS42NjR6bTAgMCIgZmlsbC1vcGFjaXR5PSIuMjM1Ii8+PHBhdGggZD0ibTkuMjcgMS40NXYuMjQ1bDMuMzgyIDMuMzgzaC4yNDZ6bTAgMCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuMzkyIi8+PHBhdGggZD0ibTkuMjcgMS40MDJhLjA0Ny4wNDcgMCAwIDAgLS4wNDcuMDQ3djIuOTY1YzAgLjM5NS4zMTYuNzExLjcxLjcxMWgyLjk2NWMuMDIgMCAuMDQtLjAxMi4wNDMtLjAzMS4wMDgtLjAxNi4wMDQtLjAzNS0uMDA3LS4wNTFsLTMuNjMtMy42MjljLS4wMDctLjAwOC0uMDIzLS4wMTItLjAzNC0uMDEyem0uMDQ2LjE2IDMuNDcgMy40N2gtMi44NTNhLjYxNC42MTQgMCAwIDEgLS42MTgtLjYxOHptMCAwIiBmaWxsPSIjMjEyMTIxIi8+PHBhdGggZD0ibTMuMzA5IDEuNDAyYS43MDguNzA4IDAgMCAwIC0uNzExLjcwN3YxMS4yNDZjMCAuMzkxLjMxNi43MDcuNzEuNzA3aDguOTNjLjM5IDAgLjcwNy0uMzE2LjcwNy0uNzA3di04LjI3N2MwLS4wMTYtLjAwNC0uMDI3LS4wMTEtLjAzNWwtMy42My0zLjYyOWMtLjAwNy0uMDA4LS4wMTktLjAxMi0uMDM0LS4wMTJ6bTAgLjA5NGg1Ljk0MWwzLjYwMiAzLjYwMnY4LjI1N2MwIC4zNC0uMjc0LjYxNC0uNjE0LjYxNGgtOC45M2EuNjEzLjYxMyAwIDAgMSAtLjYxNy0uNjE0di0xMS4yNDVjMC0uMzQuMjc0LS42MTMuNjE4LS42MTN6bTAgMCIgZmlsbD0iIzIxMjEyMSIvPjxwYXRoIGQ9Im0yMDYuMzU1LTMxLjE0M2MtMi44NzQgMC01LjI1IDIuMzc1LTUuMjUgNS4zNzV2MTAxLjc0Yy0zLjM3NC0uNzUtNi44NzQtMS4yNS0xMC42MjMtMS4yNS0xNy42MjQgMC0zMS43NDcgOS41LTMxLjc0NyAyMS4yNDggMCAxMS42MjQgMTQuMTIzIDIxLjEyMyAzMS43NDcgMjEuMTIzIDE3LjYyMyAwIDMxLjc0Ny05LjQ5OSAzMS43NDctMjEuMTIzdi0xMDUuODY1aDc0LjExOHY2NC42MmMtMy4zNzUtLjc1LTYuODc1LTEuMTI2LTEwLjYyNC0xLjEyNi0xNy42MjQgMC0zMS43NDcgOS4zNzQtMzEuNzQ3IDIxLjEyMyAwIDExLjc1IDE0LjEyMyAyMS4yNDggMzEuNzQ3IDIxLjI0OCAxNy42MjMgMCAzMS43NDctOS40OTkgMzEuNzQ3LTIxLjI0OHYtMTAwLjQ5YzAtMy0yLjM3NS01LjM3NS01LjI1LTUuMzc1em0wIDAiIGZpbGw9InVybCgjbCkiIHN0cm9rZT0iIzIxMjEyMSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjMiIHRyYW5zZm9ybT0ibWF0cml4KC4wMzEyNSAwIDAgLjAzMTI1IDAgNi43MikiLz48L3N2Zz4="); | |
| 617 | +} | |
| 618 | + | |
| 619 | +.icon-mp4, .icon-mkv, .icon-m4v, .icon-avi, .icon-ogv, .icon-mov { | |
| 620 | + background-image: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2cHQiIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2cHQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxsaW5lYXJHcmFkaWVudCBpZD0iYSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4yNzUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjguMjY3NSIgeDI9IjguMjY3NSIgeGxpbms6aHJlZj0iI2EiIHkxPSIxNC4wMTY4NzUiIHkyPSIxNC42NzgzNDM3NSIvPjxsaW5lYXJHcmFkaWVudCBpZD0iYyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4zMTQiLz48c3RvcCBvZmZzZXQ9Ii4yMjIiIHN0b3Atb3BhY2l0eT0iLjI3NSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1vcGFjaXR5PSIwIi8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9ImQiIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC0uMDM5MDcgLS4wNzAzMiAwIDI0Ljc0MiAzMS43OTgpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZSIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLjAzOTA3IC4wNzAzMiAwIC05LjE5NyAtMy43NjQpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZiIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLS4wMzkwNyAuMDcwMzIgMCAtOS4xOTcgMzEuNzk4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHI9IjIxLjE2NyIgeGxpbms6aHJlZj0iI2MiLz48cmFkaWFsR3JhZGllbnQgaWQ9ImciIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC4wMzkwNyAtLjA3MDMyIDAgMjQuNzQyIC0zLjc2NCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjciIHhsaW5rOmhyZWY9IiNjIi8+PGxpbmVhckdyYWRpZW50IGlkPSJoIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC4wMDgyNjkwMiAuMDAwMTI3MyAtLjAwMDEyNzMgLjAwODI2OTAyIDAgMCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iOTQwIiB4Mj0iOTQwIiB5MT0iMTY5NS4xMTgiIHkyPSIxNzUuMTE4Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNiMzlkZGIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNkMWM0ZTkiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iaSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguMDE0OTUgMCAwIC4wNTQ3IDcuNTIxIC04LjE4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIyNDkuMTg5IiB4Mj0iMjQ5LjE4OSIgeGxpbms6aHJlZj0iI2EiIHkxPSIyMzMuNSIgeTI9IjI1NC42NjciLz48bGluZWFyR3JhZGllbnQgaWQ9ImoiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzdlNTdjMiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzk1NzVjZCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJrIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjEwLjU4MzM0Mzc1IiB4Mj0iMTAuNTgzMzQzNzUiIHhsaW5rOmhyZWY9IiNqIiB5MT0iMTQuMDE2ODc1IiB5Mj0iMS40NDkxNTYyNSIvPjxsaW5lYXJHcmFkaWVudCBpZD0ibCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIyNDguNzA4IiB4Mj0iMjQ4LjcwOCIgeGxpbms6aHJlZj0iI2oiIHkxPSIyMzMuNSIgeTI9Ii0xNjguNjY3Ii8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmg3LjYxdi42NjRoLTcuNjF6bTAgMCIgZmlsbD0idXJsKCNiKSIvPjxwYXRoIGQ9Im0xMS41NzggMTQuMDE2aDEuMzJ2LS42NmgtMS4zMnptMCAwIiBmaWxsPSJ1cmwoI2QpIi8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmgtMS4zMjR2LjY2NGgxLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2UpIi8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmgtMS4zMjR2LS42NmgxLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2YpIi8+PHBhdGggZD0ibTExLjU3OCAxNC4wMTZoMS4zMnYuNjY0aC0xLjMyem0wIDAiIGZpbGw9InVybCgjZykiLz48cGF0aCBkPSJtOS4yNyAxLjQ1aC01Ljk2MWEuNjYuNjYgMCAwIDAgLS42NjQuNjZ2MTEuMjQ1YzAgLjM2OC4yOTYuNjYuNjY0LjY2aDguOTNjLjM2NiAwIC42Ni0uMjkyLjY2LS42NnYtOC4yNzd6bTAgMCIgZmlsbD0idXJsKCNoKSIvPjxwYXRoIGQ9Im05LjI2MiA0LjU5aDMuMTRsLjQ5Ni40OTZ2LjY2aC0zLjYzNnptMCAwIiBmaWxsPSJ1cmwoI2kpIi8+PHBhdGggZD0ibTIuNjQ1IDEzLjA5OHYuMjU3YzAgLjM2OC4yOTYuNjYuNjY0LjY2aDguOTNjLjM2NiAwIC42Ni0uMjkyLjY2LS42NnYtLjI1N2EuNjYuNjYgMCAwIDEgLS42Ni42NjRoLTguOTNhLjY2NC42NjQgMCAwIDEgLS42NjQtLjY2NHptMCAwIiBmaWxsLW9wYWNpdHk9Ii4yMzUiLz48cGF0aCBkPSJtMy4zMDkgMS40NWEuNjYuNjYgMCAwIDAgLS42NjQuNjZ2LjI1YzAtLjM2OC4yOTYtLjY2NS42NjQtLjY2NWg1Ljk2bDMuNjMgMy42M3YtLjI0N2wtMy42MzEtMy42Mjh6bTAgMCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuMzkyIi8+PHBhdGggZD0ibTEyLjg5OCA1LjA3OC0zLjYyOC0zLjYyOHYyLjk2NWMwIC4zNjcuMjk2LjY2NC42NjQuNjY0em0wIDAiIGZpbGw9InVybCgjaykiLz48cGF0aCBkPSJtOS4yNyAxLjQ1di4yNDVsMy4zODIgMy4zODNoLjI0NnptMCAwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4zOTIiLz48cGF0aCBkPSJtOS4yNyAxLjQwMmEuMDQ3LjA0NyAwIDAgMCAtLjA0Ny4wNDd2Mi45NjVjMCAuMzk1LjMxNi43MTEuNzEuNzExaDIuOTY1Yy4wMiAwIC4wNC0uMDEyLjA0My0uMDMxLjAwOC0uMDE2LjAwNC0uMDM1LS4wMDctLjA1MWwtMy42My0zLjYyOWMtLjAwNy0uMDA4LS4wMjMtLjAxMi0uMDM0LS4wMTJ6bS4wNDYuMTYgMy40NyAzLjQ3aC0yLjg1M2EuNjE0LjYxNCAwIDAgMSAtLjYxOC0uNjE4em0wIDAiIGZpbGw9IiMyMTIxMjEiLz48cGF0aCBkPSJtMy4zMDkgMS40MDJhLjcwOC43MDggMCAwIDAgLS43MTEuNzA3djExLjI0NmMwIC4zOTEuMzE2LjcwNy43MS43MDdoOC45M2MuMzkgMCAuNzA3LS4zMTYuNzA3LS43MDd2LTguMjc3YzAtLjAxNi0uMDA0LS4wMjctLjAxMS0uMDM1bC0zLjYzLTMuNjI5Yy0uMDA3LS4wMDgtLjAxOS0uMDEyLS4wMzQtLjAxMnptMCAuMDk0aDUuOTQxbDMuNjAyIDMuNjAydjguMjU3YzAgLjM0LS4yNzQuNjE0LS42MTQuNjE0aC04LjkzYS42MTMuNjEzIDAgMCAxIC0uNjE3LS42MTR2LTExLjI0NWMwLS4zNC4yNzQtLjYxMy42MTgtLjYxM3ptMCAwIiBmaWxsPSIjMjEyMTIxIi8+PHBhdGggZD0ibTIwMS4xMDYtMzEuMTQzYy01Ljg3NS4xMjUtMTAuNjI0IDQuNzUtMTAuNjI0IDEwLjYyNHYxMjYuOTg5YzAgOCA4LjYyNCAxMy4xMjQgMTUuNjIzIDkuMjVsMTE2LjQ5LTYzLjQ5NWM3LjM3My00IDcuMzczLTE0LjQ5OCAwLTE4LjQ5OGwtMTE2LjQ5LTYzLjQ5NGMtMS41LS44NzUtMy4yNS0xLjM3NS01LTEuMzc1em0wIDAiIHN0eWxlPSJmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlOiMyMTIxMjE7ZmlsbDp1cmwoI2wpIiB0cmFuc2Zvcm09Im1hdHJpeCguMDMxMjUgMCAwIC4wMzEyNSAwIDYuNzIpIi8+PC9zdmc+"); | |
| 621 | +} | |
| 622 | + | |
| 623 | +.icon-pdf { | |
| 624 | + background-image: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2cHQiIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2cHQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxsaW5lYXJHcmFkaWVudCBpZD0iYSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSI3LjkzNzUiIHgyPSI3LjkzNzUiIHkxPSIxNC4wMTY4NzUiIHkyPSIxNC42NzgzNDM3NSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4yNzUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3Atb3BhY2l0eT0iLjMxNCIvPjxzdG9wIG9mZnNldD0iLjIyMiIgc3RvcC1vcGFjaXR5PSIuMjc1Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9IjAiLz48L2xpbmVhckdyYWRpZW50PjxyYWRpYWxHcmFkaWVudCBpZD0iYyIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLS4wMzkwNyAtLjA3MDMyIDAgMjUuMDczIDMxLjc5OCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjciIHhsaW5rOmhyZWY9IiNiIi8+PHJhZGlhbEdyYWRpZW50IGlkPSJkIiBjeD0iNDUwLjkwOSIgY3k9IjE4OS41NzkiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMCAuMDM5MDcgLjA3MDMyIDAgLTkuMTk3IC0zLjc2NCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjciIHhsaW5rOmhyZWY9IiNiIi8+PHJhZGlhbEdyYWRpZW50IGlkPSJlIiBjeD0iNDUwLjkwOSIgY3k9IjE4OS41NzkiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMCAtLjAzOTA3IC4wNzAzMiAwIC05LjE5NyAzMS43OTgpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYiIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZiIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLjAzOTA3IC0uMDcwMzIgMCAyNS4wNzMgLTMuNzY0KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHI9IjIxLjE2NyIgeGxpbms6aHJlZj0iI2IiLz48bGluZWFyR3JhZGllbnQgaWQ9ImciIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLjAzNzY3IDAgMCAuMTA4NyAtMi45MjcgMTYuODIpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjMwNi45MTciIHgyPSIzMDUuOTU1IiB5MT0iLTI1Ljc5MiIgeTI9Ii0xNDEuNDE1Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNlZjlhOWEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmNkZDIiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iaCI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZWY1MzUwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZTU3MzczIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImkiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoNC41NTYxNCAwIDAgMTMuMTQ2MTggLTM1My45NzMgMjAzNC4xOCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMTY1LjQ4NSIgeDI9IjE2NS40ODUiIHhsaW5rOmhyZWY9IiNoIiB5MT0iLTI1Ljc5MiIgeTI9Ii0xNDEuNDE1Ii8+PGxpbmVhckdyYWRpZW50IGlkPSJqIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ijc2MCIgeDI9Ijc2MCIgeGxpbms6aHJlZj0iI2giIHkxPSIxNjk1LjExNyIgeTI9IjE3NS4xMTciLz48cGF0aCBkPSJtMy45NjkgMTQuMDE2aDcuOTM3di42NjRoLTcuOTM2em0wIDAiIGZpbGw9InVybCgjYSkiLz48cGF0aCBkPSJtMTEuOTA2IDE0LjAxNmgxLjMyNHYtLjY2aC0xLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2MpIi8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmgtMS4zMjR2LjY2NGgxLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2QpIi8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmgtMS4zMjR2LS42NmgxLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2UpIi8+PHBhdGggZD0ibTExLjkwNiAxNC4wMTZoMS4zMjR2LjY2NGgtMS4zMjR6bTAgMCIgZmlsbD0idXJsKCNmKSIvPjxwYXRoIGQ9Im0zLjMwOSAxLjQ1aDkuMjYxYy4zNjQgMCAuNjYuMjk2LjY2LjY2djExLjI0NWEuNjYuNjYgMCAwIDEgLS42Ni42NmgtOS4yNmEuNjYuNjYgMCAwIDEgLS42NjQtLjY2di0xMS4yNDVjMC0uMzYzLjI5Ni0uNjYuNjY0LS42NnptMCAwIiBmaWxsPSJ1cmwoI2cpIi8+PHBhdGggZD0ibTQwMC4xMiAxNzUuMjU5Yy00NC40MDYgMC04MC4zMDggMzUuNDMtODAuMzA4IDc5LjgzNXYxMzYwLjAyNmMwIDQ0LjQwNiAzNS45MDIgNzkuODM1IDgwLjMwNyA3OS44MzVoMTE5Ljk4OXYtMTUxOS42OTV6bTAgMCIgZmlsbD0idXJsKCNpKSIgc3Ryb2tlPSIjMjEyMTIxIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS13aWR0aD0iMTEuMzM5IiB0cmFuc2Zvcm09Im1hdHJpeCguMDA4MjY5MDIgLjAwMDEyNzMgLS4wMDAxMjczIC4wMDgyNjkwMiAwIDApIi8+PHBhdGggZD0ibTMuMzA5IDEzLjk4YS42NjQuNjY0IDAgMCAxIC0uNjY0LS42NjR2LS4wODJjMCAuMzY4LjI5Ni42NjQuNjY0LjY2NGg5LjI2MWMuMzY0IDAgLjY2LS4yOTYuNjYtLjY2NHYuMDgyYS42NjMuNjYzIDAgMCAxIC0uNjYuNjY0em0wIDAiIGZpbGwtb3BhY2l0eT0iLjE5NiIvPjxwYXRoIGQ9Im02NzkuNzc4IDE3NS4yNTl2OTU5LjkwOGM1OS45OTQtMTYwLjE0MiAxMDAuMTQ4LTE2MC4xNDIgMTYwLjE0MiAwdi05NTkuOTA3em0wIDAiIGZpbGw9InVybCgjaikiIHN0cm9rZT0iIzIxMjEyMSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjExLjMzOSIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwODI2OTAyIC4wMDAxMjczIC0uMDAwMTI3MyAuMDA4MjY5MDIgMCAwKSIvPjxwYXRoIGQ9Im0zLjMwOSAxLjQ4NGEuNjY0LjY2NCAwIDAgMCAtLjY2NC42NjR2LjA4MmMwLS4zNjcuMjk2LS42NjQuNjY0LS42NjRoOS4yNjFjLjM2NCAwIC42Ni4yOTcuNjYuNjY0di0uMDgyYS42NjMuNjYzIDAgMCAwIC0uNjYtLjY2NHptMCAwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4zOTIiLz48cGF0aCBkPSJtMy4zMDkgMS40MDJhLjcwOC43MDggMCAwIDAgLS43MTEuNzA3djExLjI0NmMwIC4zOTEuMzE2LjcwNy43MS43MDdoOS4yNjJjLjM5IDAgLjcwNy0uMzE2LjcwNy0uNzA3di0xMS4yNDVhLjcwNy43MDcgMCAwIDAgLS43MDctLjcwN3ptMCAuMDk0aDkuMjYxYy4zNCAwIC42MTQuMjc0LjYxNC42MTN2MTEuMjQ2YzAgLjM0LS4yNzQuNjE0LS42MTQuNjE0aC05LjI2YS42MTMuNjEzIDAgMCAxIC0uNjE4LS42MTR2LTExLjI0NWMwLS4zNC4yNzQtLjYxMy42MTgtLjYxM3ptMCAwIiBmaWxsPSIjMjEyMTIxIi8+PC9zdmc+"); | |
| 625 | +} | |
| 626 | + | |
| 627 | +.icon-ppt, .icon-pptx { | |
| 628 | + background-image: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2cHQiIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2cHQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxsaW5lYXJHcmFkaWVudCBpZD0iYSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4yNzUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjguMjY3NSIgeDI9IjguMjY3NSIgeGxpbms6aHJlZj0iI2EiIHkxPSIxNC4wMTY4NzUiIHkyPSIxNC42NzgzNDM3NSIvPjxsaW5lYXJHcmFkaWVudCBpZD0iYyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4zMTQiLz48c3RvcCBvZmZzZXQ9Ii4yMjIiIHN0b3Atb3BhY2l0eT0iLjI3NSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1vcGFjaXR5PSIwIi8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9ImQiIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC0uMDM5MDcgLS4wNzAzMiAwIDI0Ljc0MiAzMS43OTgpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZSIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLjAzOTA3IC4wNzAzMiAwIC05LjE5NyAtMy43NjQpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZiIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLS4wMzkwNyAuMDcwMzIgMCAtOS4xOTcgMzEuNzk4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHI9IjIxLjE2NyIgeGxpbms6aHJlZj0iI2MiLz48cmFkaWFsR3JhZGllbnQgaWQ9ImciIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC4wMzkwNyAtLjA3MDMyIDAgMjQuNzQyIC0zLjc2NCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjciIHhsaW5rOmhyZWY9IiNjIi8+PGxpbmVhckdyYWRpZW50IGlkPSJoIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC4wMDgyNjkwMiAuMDAwMTI3MyAtLjAwMDEyNzMgLjAwODI2OTAyIDAgMCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iOTQwIiB4Mj0iOTQwIiB5MT0iMTY5NS4xMTgiIHkyPSIxNzUuMTE4Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNmZmFiOTEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmNjYmMiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iaSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguMDE0OTUgMCAwIC4wNTQ3IDcuNTIxIC04LjE4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIyNDkuMTg5IiB4Mj0iMjQ5LjE4OSIgeGxpbms6aHJlZj0iI2EiIHkxPSIyMzMuNSIgeTI9IjI1NC42NjciLz48bGluZWFyR3JhZGllbnQgaWQ9ImoiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2ZmNzA0MyIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2ZmOGE2NSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJrIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjEwLjU4MzM0Mzc1IiB4Mj0iMTAuNTgzMzQzNzUiIHhsaW5rOmhyZWY9IiNqIiB5MT0iMTQuMDE2ODc1IiB5Mj0iMS40NDkxNTYyNSIvPjxmaWx0ZXIgaWQ9ImwiIGhlaWdodD0iMTAwJSIgd2lkdGg9IjEwMCUiIHg9IjAlIiB5PSIwJSI+PGZlQ29sb3JNYXRyaXggaW49IlNvdXJjZUdyYXBoaWMiIHZhbHVlcz0iMCAwIDAgMCAxIDAgMCAwIDAgMSAwIDAgMCAwIDEgMCAwIDAgMSAwIi8+PC9maWx0ZXI+PG1hc2sgaWQ9Im0iPjxwYXRoIGQ9Im0wIDBoMTZ2MTZoLTE2eiIgZmlsbC1vcGFjaXR5PSIuOTg4IiBmaWx0ZXI9InVybCgjbCkiLz48L21hc2s+PG1hc2sgaWQ9Im4iPjxwYXRoIGQ9Im0wIDBoMTZ2MTZoLTE2eiIgZmlsbC1vcGFjaXR5PSIuOTg4IiBmaWx0ZXI9InVybCgjbCkiLz48L21hc2s+PGxpbmVhckdyYWRpZW50IGlkPSJvIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ijk1OS45ODU2NDkyNiIgeDI9Ijk1OS45ODU2NDkyNiIgeGxpbms6aHJlZj0iI2oiIHkxPSIxNjk1LjA5NDAzNTUyIiB5Mj0iMTc1LjExMzE1NTk2Ii8+PGNsaXBQYXRoIGlkPSJwIj48cGF0aCBkPSJtMCAwaDE2djE2aC0xNnoiLz48L2NsaXBQYXRoPjxtYXNrIGlkPSJxIj48cGF0aCBkPSJtMCAwaDE2djE2aC0xNnoiIGZpbGwtb3BhY2l0eT0iLjk4OCIgZmlsdGVyPSJ1cmwoI2wpIi8+PC9tYXNrPjxsaW5lYXJHcmFkaWVudCBpZD0iciIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSI2My40OTY4IiB4Mj0iNjMuNDk2OCIgeGxpbms6aHJlZj0iI2oiIHkxPSIyMzMuNDk0ODkwMDgiIHkyPSItMTY0LjEzMTI3OTU2Ii8+PGNsaXBQYXRoIGlkPSJzIj48cGF0aCBkPSJtMCAwaDE2djE2aC0xNnoiLz48L2NsaXBQYXRoPjxjbGlwUGF0aCBpZD0idCI+PHBhdGggZD0ibTAgMGgxNnYxNmgtMTZ6Ii8+PC9jbGlwUGF0aD48cGF0aCBkPSJtMy45NjkgMTQuMDE2aDcuNjF2LjY2NGgtNy42MXptMCAwIiBmaWxsPSJ1cmwoI2IpIi8+PHBhdGggZD0ibTExLjU3OCAxNC4wMTZoMS4zMnYtLjY2aC0xLjMyem0wIDAiIGZpbGw9InVybCgjZCkiLz48cGF0aCBkPSJtMy45NjkgMTQuMDE2aC0xLjMyNHYuNjY0aDEuMzI0em0wIDAiIGZpbGw9InVybCgjZSkiLz48cGF0aCBkPSJtMy45NjkgMTQuMDE2aC0xLjMyNHYtLjY2aDEuMzI0em0wIDAiIGZpbGw9InVybCgjZikiLz48cGF0aCBkPSJtMTEuNTc4IDE0LjAxNmgxLjMydi42NjRoLTEuMzJ6bTAgMCIgZmlsbD0idXJsKCNnKSIvPjxwYXRoIGQ9Im05LjI3IDEuNDVoLTUuOTYxYS42Ni42NiAwIDAgMCAtLjY2NC42NnYxMS4yNDVjMCAuMzY4LjI5Ni42Ni42NjQuNjZoOC45M2MuMzY2IDAgLjY2LS4yOTIuNjYtLjY2di04LjI3N3ptMCAwIiBmaWxsPSJ1cmwoI2gpIi8+PHBhdGggZD0ibTIuNjQ1IDEzLjA5OHYuMjU3YzAgLjM2OC4yOTYuNjYuNjY0LjY2aDguOTNjLjM2NiAwIC42Ni0uMjkyLjY2LS42NnYtLjI1N2EuNjYuNjYgMCAwIDEgLS42Ni42NjRoLTguOTNhLjY2NC42NjQgMCAwIDEgLS42NjQtLjY2NHptMCAwIiBmaWxsLW9wYWNpdHk9Ii4yMzUiLz48cGF0aCBkPSJtOS4yNjIgNC41OWgzLjE0bC40OTYuNDk2di42NmgtMy42MzZ6bTAgMCIgZmlsbD0idXJsKCNpKSIvPjxwYXRoIGQ9Im0zLjMwOSAxLjQ1YS42Ni42NiAwIDAgMCAtLjY2NC42NnYuMjVjMC0uMzY4LjI5Ni0uNjY1LjY2NC0uNjY1aDUuOTZsMy42MyAzLjYzdi0uMjQ3bC0zLjYzMS0zLjYyOHptMCAwIiBmaWxsPSIjZjZmNmY2IiBmaWxsLW9wYWNpdHk9Ii41ODgiLz48cGF0aCBkPSJtMTIuODk4IDUuMDc4LTMuNjI4LTMuNjI4djIuOTY1YzAgLjM2Ny4yOTYuNjY0LjY2NC42NjR6bTAgMCIgZmlsbD0idXJsKCNrKSIvPjxwYXRoIGQ9Im05LjI3IDQuMjU4di4xNTZjMCAuMzY3LjI5Ni42NjQuNjY0LjY2NGgyLjk2NGwtLjE1Ni0uMTU2aC0yLjgwOGEuNjY0LjY2NCAwIDAgMSAtLjY2NC0uNjY0em0wIDAiIGZpbGwtb3BhY2l0eT0iLjIzNSIvPjxwYXRoIGQ9Im05LjI3IDEuNDV2LjI0NWwzLjM4MiAzLjM4M2guMjQ2em0wIDAiIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iLjM5MiIvPjxwYXRoIGQ9Im05LjI3IDEuNDAyYS4wNDcuMDQ3IDAgMCAwIC0uMDQ3LjA0N3YyLjk2NWMwIC4zOTUuMzE2LjcxMS43MS43MTFoMi45NjVjLjAyIDAgLjA0LS4wMTIuMDQzLS4wMzEuMDA4LS4wMTYuMDA0LS4wMzUtLjAwNy0uMDUxbC0zLjYzLTMuNjI5Yy0uMDA3LS4wMDgtLjAyMy0uMDEyLS4wMzQtLjAxMnptLjA0Ni4xNiAzLjQ3IDMuNDdoLTIuODUzYS42MTQuNjE0IDAgMCAxIC0uNjE4LS42MTh6bTAgMCIgZmlsbD0iIzIxMjEyMSIvPjxwYXRoIGQ9Im0zLjMwOSAxLjQwMmEuNzA4LjcwOCAwIDAgMCAtLjcxMS43MDd2MTEuMjQ2YzAgLjM5MS4zMTYuNzA3LjcxLjcwN2g4LjkzYy4zOSAwIC43MDctLjMxNi43MDctLjcwN3YtOC4yNzdjMC0uMDE2LS4wMDQtLjAyNy0uMDExLS4wMzVsLTMuNjMtMy42MjljLS4wMDctLjAwOC0uMDE5LS4wMTItLjAzNC0uMDEyem0wIC4wOTRoNS45NDFsMy42MDIgMy42MDJ2OC4yNTdjMCAuMzQtLjI3NC42MTQtLjYxNC42MTRoLTguOTNhLjYxMy42MTMgMCAwIDEgLS42MTctLjYxNHYtMTEuMjQ1YzAtLjM0LjI3NC0uNjEzLjYxOC0uNjEzem0wIDAiIGZpbGw9IiMyMTIxMjEiLz48ZyBjbGlwLXBhdGg9InVybCgjdCkiIG1hc2s9InVybCgjbSkiPjxnIGNsaXAtcGF0aD0idXJsKCNwKSIgbWFzaz0idXJsKCNuKSI+PHBhdGggZD0ibTkwOS44MzQgNzY4LjExNmMtMTcxLjQ4IDAtMzA5Ljg5MSAxMDguNjUtMzA5Ljg5MSAyNDMuMjg0IDAgMTM1LjEwNSAxMzguNDEyIDI0My43NTYgMzA5Ljg5MSAyNDMuNzU2IDE3MC41MzUgMCAzMDcuNTMtMTA2Ljc2MiAzMDkuODkyLTIzOS45NzdoLTI4OS41NzljLTEuNDE3IDAtMi44MzQgMC00LjI1MS0uNDczLTEuNDE3LS40NzItMi4zNjItLjQ3Mi0zLjc4LS45NDQtLjk0NC0uOTQ1LTIuMzYxLTEuNDE4LTMuMzA2LTEuODlsLTIuODM0LTIuODM0Yy0uOTQ1LS40NzMtMS44OS0xLjg5LTIuMzYyLTIuODM1LS45NDUtLjk0NS0xLjQxOC0xLjg5LTEuODktMy4zMDctLjQ3Mi0uOTQ0LS45NDUtMi4zNjItMS40MTctMy43NzkgMC0xLjQxNy0uNDczLTIuODM0LS40NzMtMy43Nzl6bTAgMCIgZmlsbD0idXJsKCNvKSIgc3Ryb2tlPSIjMjEyMTIxIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMTEuMzM5IiB0cmFuc2Zvcm09Im1hdHJpeCguMDA4MjY5MDIgLjAwMDEyNzMgLS4wMDAxMjczIC4wMDgyNjkwMiAwIDApIi8+PC9nPjxnIGNsaXAtcGF0aD0idXJsKCNzKSIgbWFzaz0idXJsKCNxKSI+PHBhdGggZD0ibTI1Ni42LTI1Ljc2OHY2MC4xMmMwIC4zNzQuMTI1LjYyNC4xMjUgMSAuMTI1LjM3NC4yNS43NDkuMzc1Ljk5OS4xMjUuMzc1LjI1LjYyNS41Ljg3NS4xMjUuMzc1LjM3NS42MjUuNjI1Ljg3NXMuNS4zNzUuNzUuNjI1Yy4yNS4xMjUuNjI1LjM3NS44NzUuNS4zNzUuMTI1LjYyNS4yNSAxIC4yNS4zNzUuMTI1Ljc1LjEyNSAxLjEyNS4xMjVoNzYuNjE4YzAtLjM3NS4xMjUtLjYyNS4xMjUtMSAwLTM1LjYyMi0zNi42MjItNjQuMzctODIuMTE4LTY0LjM3em0wIDAiIGZpbGw9InVybCgjcikiIHN0cm9rZT0iIzIxMjEyMSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjMiIHRyYW5zZm9ybT0ibWF0cml4KC4wMzEyNSAwIDAgLjAzMTI1IDAgNi43MikiLz48L2c+PC9nPjwvc3ZnPg=="); | |
| 629 | +} | |
| 630 | + | |
| 631 | +.icon-srt { | |
| 632 | + background-image: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGxpbmVhckdyYWRpZW50IGlkPSJhIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjcuOTM4MjYiIHgyPSI3LjkzODI2IiB5MT0iMTQuMDE2OTY4NSIgeTI9IjE0LjY3ODQ5MDUwMDAyIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3Atb3BhY2l0eT0iLjI3NDUxIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9IjAiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYiI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4zMTM3MjYiLz48c3RvcCBvZmZzZXQ9Ii4yMjIyMjIiIHN0b3Atb3BhY2l0eT0iLjI3NDUxIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9IjAiLz48L2xpbmVhckdyYWRpZW50PjxyYWRpYWxHcmFkaWVudCBpZD0iYyIgY3g9IjQ1MC45MDg5MzYiIGN5PSIxODkuNTc5MzYxIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC4wMDAwMDAwOSAtLjAzOTA2NjIgLS4wNzAzMTkyIC0uMDAwMDAwMDUgMjUuMDczMDQzIDMxLjc5NzY2NikiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjY2NTYiIHhsaW5rOmhyZWY9IiNiIi8+PHJhZGlhbEdyYWRpZW50IGlkPSJkIiBjeD0iNDUwLjkwODkzNiIgY3k9IjE4OS41NzkzNjEiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLS4wMDAwMDAwOSAuMDM5MDY2MiAuMDcwMzE5MiAuMDAwMDAwMDUgLTkuMTk2NTI0IC0zLjc2Mzc0KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHI9IjIxLjE2NjY1NiIgeGxpbms6aHJlZj0iI2IiLz48cmFkaWFsR3JhZGllbnQgaWQ9ImUiIGN4PSI0NTAuOTA4OTM2IiBjeT0iMTg5LjU3OTM2MSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtLjAwMDAwMDA5IC0uMDM5MDY2MiAuMDcwMzE5MiAtLjAwMDAwMDA1IC05LjE5NjUyNCAzMS43OTc2NjYpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY2NjU2IiB4bGluazpocmVmPSIjYiIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZiIgY3g9IjQ1MC45MDg5MzYiIGN5PSIxODkuNTc5MzYxIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC4wMDAwMDAwOSAuMDM5MDY2MiAtLjA3MDMxOTIgLjAwMDAwMDA1IDI1LjA3MzA0MiAtMy43NjM3NCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjY2NTYiIHhsaW5rOmhyZWY9IiNiIi8+PGxpbmVhckdyYWRpZW50IGlkPSJnIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC4wMzc2NzQ4IDAgMCAuMTA4NzA2IC0yLjkyNzAwNyAxNi44MjA2NzQpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjMwNi45MTY2NTYiIHgyPSIzMDUuOTU0NTU5IiB5MT0iLTI1Ljc5MTY3NiIgeTI9Ii0xNDEuNDE0NTgxIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiM4MGNiYzQiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNiMmRmZGIiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iaCI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMjZhNjlhIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNGRiNmFjIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImkiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoNC41NTYxNDMgMCAwIDEzLjE0NjE4NCAtMzUzLjk3MjY1NiAyMDM0LjE4MDE3NikiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMTY1LjQ4NDg0OCIgeDI9IjE2NS40ODQ4NDgiIHhsaW5rOmhyZWY9IiNoIiB5MT0iLTI1Ljc5MTY3MiIgeTI9Ii0xNDEuNDE0NTgxIi8+PGxpbmVhckdyYWRpZW50IGlkPSJqIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjguMjY5MDIzMjUwMDMiIHgyPSI4LjI2OTAyMzI1MDAzIiB4bGluazpocmVmPSIjaCIgeTE9IjE0LjAxNjk2NzUiIHkyPSIxLjQ0ODA1MzAwMDAzIi8+PHBhdGggZD0ibTMuOTY4NzUgMTQuMDE1NjI1aDcuOTM3NXYuNjY0MDYzaC03LjkzNzV6bTAgMCIgZmlsbD0idXJsKCNhKSIvPjxwYXRoIGQ9Im0xMS45MDYyNSAxNC4wMTU2MjVoMS4zMjQyMTl2LS42NjAxNTZoLTEuMzI0MjE5em0wIDAiIGZpbGw9InVybCgjYykiLz48cGF0aCBkPSJtMy45Njg3NSAxNC4wMTU2MjVoLTEuMzI0MjE5di42NjQwNjNoMS4zMjQyMTl6bTAgMCIgZmlsbD0idXJsKCNkKSIvPjxwYXRoIGQ9Im0zLjk2ODc1IDE0LjAxNTYyNWgtMS4zMjQyMTl2LS42NjAxNTZoMS4zMjQyMTl6bTAgMCIgZmlsbD0idXJsKCNlKSIvPjxwYXRoIGQ9Im0xMS45MDYyNSAxNC4wMTU2MjVoMS4zMjQyMTl2LjY2NDA2M2gtMS4zMjQyMTl6bTAgMCIgZmlsbD0idXJsKCNmKSIvPjxwYXRoIGQ9Im0zLjMwODU5NCAxLjQ0OTIxOWg5LjI2MTcxOGMuMzYzMjgyIDAgLjY2MDE1Ny4yOTY4NzUuNjYwMTU3LjY2MDE1NnYxMS4yNDYwOTRjMCAuMzY3MTg3LS4yOTY4NzUuNjYwMTU2LS42NjAxNTcuNjYwMTU2aC05LjI2MTcxOGMtLjM2NzE4OCAwLS42NjQwNjMtLjI5Mjk2OS0uNjY0MDYzLS42NjAxNTZ2LTExLjI0NjA5NGMwLS4zNjMyODEuMjk2ODc1LS42NjAxNTYuNjY0MDYzLS42NjAxNTZ6bTAgMCIgZmlsbD0idXJsKCNnKSIvPjxwYXRoIGQ9Im00MDAuMTE5MjkyIDE3NS4yNTg4NjNjLTQ0LjQwNTIxMSAwLTgwLjMwNzI5NiAzNS40Mjk2OS04MC4zMDcyOTYgNzkuODM0OXYxMzYwLjAyNzY3NmMwIDQ0LjQwNTIxMSAzNS45MDIwODUgNzkuODM0OSA4MC4zMDcyOTYgNzkuODM0OWgxMTkuOTg4NTQ4di0xNTE5LjY5NzQ3NnptMCAwIiBmaWxsPSJ1cmwoI2kpIiBzdHJva2U9IiMyMTIxMjEiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIxMS4zMzg1ODMiIHRyYW5zZm9ybT0ic2NhbGUoLjAwODI2OTAyKSIvPjxwYXRoIGQ9Im0zLjMwODU5NCAxMy45ODA0NjljLS4zNjcxODggMC0uNjY0MDYzLS4yOTY4NzUtLjY2NDA2My0uNjY0MDYzdi0uMDgyMDMxYzAgLjM2NzE4Ny4yOTY4NzUuNjY0MDYzLjY2NDA2My42NjQwNjNoOS4yNjE3MThjLjM2MzI4MiAwIC42NjAxNTctLjI5Njg3Ni42NjAxNTctLjY2NDA2M3YuMDgyMDMxYzAgLjM2NzE4OC0uMjk2ODc1LjY2NDA2My0uNjYwMTU3LjY2NDA2M3ptMCAwIiBmaWxsLW9wYWNpdHk9Ii4xOTYwNzgiLz48cGF0aCBkPSJtMy4zMDg1OTQgMS40ODQzNzVjLS4zNjcxODggMC0uNjY0MDYzLjI5Njg3NS0uNjY0MDYzLjY2NDA2M3YuMDgyMDMxYzAtLjM2NzE4OC4yOTY4NzUtLjY2NDA2My42NjQwNjMtLjY2NDA2M2g5LjI2MTcxOGMuMzYzMjgyIDAgLjY2MDE1Ny4yOTY4NzUuNjYwMTU3LjY2NDA2M3YtLjA4MjAzMWMwLS4zNjcxODgtLjI5Njg3NS0uNjY0MDYzLS42NjAxNTctLjY2NDA2M3ptMCAwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4zOTIxNTciLz48cGF0aCBkPSJtMy4zMDg1OTQgMS40MDIzNDRjLS4zOTQ1MzIgMC0uNzEwOTM4LjMxNjQwNi0uNzEwOTM4LjcwNzAzMXYxMS4yNDYwOTRjMCAuMzkwNjI1LjMxNjQwNi43MDcwMzEuNzEwOTM4LjcwNzAzMWg5LjI2MTcxOGMuMzkwNjI2IDAgLjcwNzAzMi0uMzE2NDA2LjcwNzAzMi0uNzA3MDMxdi0xMS4yNDYwOTRjMC0uMzkwNjI1LS4zMTY0MDYtLjcwNzAzMS0uNzA3MDMyLS43MDcwMzF6bTAgLjA5Mzc1aDkuMjYxNzE4Yy4zMzk4NDQgMCAuNjEzMjgyLjI3MzQzNy42MTMyODIuNjEzMjgxdjExLjI0NjA5NGMwIC4zMzk4NDMtLjI3MzQzOC42MTMyODEtLjYxMzI4Mi42MTMyODFoLTkuMjYxNzE4Yy0uMzQzNzUgMC0uNjE3MTg4LS4yNzM0MzgtLjYxNzE4OC0uNjEzMjgxdi0xMS4yNDYwOTRjMC0uMzM5ODQ0LjI3MzQzOC0uNjEzMjgxLjYxNzE4OC0uNjEzMjgxem0wIDAiIGZpbGw9IiMyMTIxMjEiLz48cGF0aCBkPSJtNi4xMTcxODggNC43NTM5MDZjLS4yNzM0MzggMC0uNDk2MDk0LjIyMjY1Ni0uNDk2MDk0LjQ5NjA5NHYzLjY0MDYyNWMwIC4yNzM0MzcuMjIyNjU2LjQ5NjA5NC40OTYwOTQuNDk2MDk0aDMuNjcxODc0bDEuMjIyNjU3IDEuMjI2NTYyYy4yMDcwMzEuMjA3MDMxLjU2MjUuMDU4NTk0LjU2MjUtLjIzNDM3NXYtMS4wMTk1MzFjLjE5NTMxMi0uMDY2NDA2LjMzMjAzMS0uMjUuMzMyMDMxLS40Njg3NXYtMy42NDA2MjVjMC0uMjczNDM4LS4yMTg3NS0uNDk2MDk0LS40OTYwOTQtLjQ5NjA5NHptMCAwIiBmaWxsPSJ1cmwoI2opIi8+PHBhdGggZD0ibTYuMTE3MTg4IDQuNzUzOTA2Yy0uMjczNDM4IDAtLjQ5NjA5NC4yMjI2NTYtLjQ5NjA5NC40OTYwOTR2LjE2Nzk2OWMwLS4yNzczNDQuMjIyNjU2LS40OTYwOTQuNDk2MDk0LS40OTYwOTRoNS4yOTI5NjhjLjI3NzM0NCAwIC40OTYwOTQuMjE4NzUuNDk2MDk0LjQ5NjA5NHYtLjE2Nzk2OWMwLS4yNzM0MzgtLjIxODc1LS40OTYwOTQtLjQ5NjA5NC0uNDk2MDk0em0wIDAiIGZpbGwtb3BhY2l0eT0iLjIzNTI5NCIvPjxwYXRoIGQ9Im01LjYyMTA5NCA4LjkxNzk2OXYuMTM2NzE5YzAgLjI3NzM0My4yMjI2NTYuNDk2MDkzLjQ5NjA5NC40OTYwOTNoMy42NzE4NzRsMS4yMjI2NTcgMS4yMjY1NjNjLjIwNzAzMS4yMDcwMzEuNTYyNS4wNTg1OTQuNTYyNS0uMjM0Mzc1di0uMTM2NzE5YzAgLjI5Mjk2OS0uMzU1NDY5LjQ0MTQwNi0uNTYyNS4yMzQzNzVsLTEuMjIyNjU3LTEuMjI2NTYzaC0zLjY3MTg3NGMtLjI3MzQzOCAwLS40OTYwOTQtLjIyMjY1Ni0uNDk2MDk0LS40OTYwOTN6bTYuMjg1MTU2IDBjMCAuMjE4NzUtLjEzNjcxOS40MDIzNDMtLjMzMjAzMS40Njg3NXYuMTM2NzE5Yy4xOTUzMTItLjA2NjQwNy4zMzIwMzEtLjI1LjMzMjAzMS0uNDY4NzV6bTAgMCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuMzkyMTU3Ii8+PHBhdGggZD0ibTE5NS43MzEzMDktNjIuODg5NTI2Yy04Ljc0OTE2NSAwLTE1Ljg3MzQ4NSA3LjEyNDMyLTE1Ljg3MzQ4NSAxNS44NzM0ODR2MTE2LjQ4ODg3NmMwIDguNzQ5MTY1IDcuMTI0MzIgMTUuODczNDg0IDE1Ljg3MzQ4NSAxNS44NzM0ODRoMTE3LjQ4ODc4bDM5LjEyMTI2NCAzOS4yNDYyNTNjNi42MjQzNjcgNi42MjQzNjcgMTcuOTk4MjgxIDEuODc0ODIgMTcuOTk4MjgxLTcuNDk5Mjg0di0zMi42MjE4ODVjNi4yNDk0MDQtMi4xMjQ3OTcgMTAuNjIzOTg2LTcuOTk5MjM2IDEwLjYyMzk4Ni0xNC45OTg1Njh2LTExNi40ODg4NzZjMC04Ljc0OTE2NC02Ljk5OTMzMi0xNS44NzM0ODQtMTUuODczNDg0LTE1Ljg3MzQ4NHptMCAwIiBmaWxsPSJub25lIiBzdHJva2U9IiMyMTIxMjEiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzIiB0cmFuc2Zvcm09Im1hdHJpeCguMDMxMjUzIDAgMCAuMDMxMjUzIDAgNi43MTkzOTIpIi8+PC9zdmc+"); | |
| 633 | +} | |
| 634 | + | |
| 635 | +.icon-xls, .icon-xlsx, .icon-csv { | |
| 636 | + background-image: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2cHQiIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2cHQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxsaW5lYXJHcmFkaWVudCBpZD0iYSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4yNzUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjguMjY3NSIgeDI9IjguMjY3NSIgeGxpbms6aHJlZj0iI2EiIHkxPSIxNC4wMTY4NzUiIHkyPSIxNC42NzgzNDM3NSIvPjxsaW5lYXJHcmFkaWVudCBpZD0iYyI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4zMTQiLz48c3RvcCBvZmZzZXQ9Ii4yMjIiIHN0b3Atb3BhY2l0eT0iLjI3NSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1vcGFjaXR5PSIwIi8+PC9saW5lYXJHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9ImQiIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC0uMDM5MDcgLS4wNzAzMiAwIDI0Ljc0MiAzMS43OTgpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZSIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLjAzOTA3IC4wNzAzMiAwIC05LjE5NyAtMy43NjQpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYyIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZiIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLS4wMzkwNyAuMDcwMzIgMCAtOS4xOTcgMzEuNzk4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHI9IjIxLjE2NyIgeGxpbms6aHJlZj0iI2MiLz48cmFkaWFsR3JhZGllbnQgaWQ9ImciIGN4PSI0NTAuOTA5IiBjeT0iMTg5LjU3OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwIC4wMzkwNyAtLjA3MDMyIDAgMjQuNzQyIC0zLjc2NCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjciIHhsaW5rOmhyZWY9IiNjIi8+PGxpbmVhckdyYWRpZW50IGlkPSJoIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC4wMDgyNjkwMiAuMDAwMTI3MyAtLjAwMDEyNzMgLjAwODI2OTAyIDAgMCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iOTQwIiB4Mj0iOTQwIiB5MT0iMTY5NS4xMTgiIHkyPSIxNzUuMTE4Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiM4MGNiYzQiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNiMmRmZGIiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iaSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguMDE0OTUgMCAwIC4wNTQ3IDcuNTIxIC04LjE4KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIyNDkuMTg5IiB4Mj0iMjQ5LjE4OSIgeGxpbms6aHJlZj0iI2EiIHkxPSIyMzMuNSIgeTI9IjI1NC42NjciLz48bGluZWFyR3JhZGllbnQgaWQ9ImoiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzI2YTY5YSIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzRkYjZhYyIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJrIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjEwLjU4MzM0Mzc1IiB4Mj0iMTAuNTgzMzQzNzUiIHhsaW5rOmhyZWY9IiNqIiB5MT0iMTQuMDE2ODc1IiB5Mj0iMS40NDkxNTYyNSIvPjxmaWx0ZXIgaWQ9ImwiIGhlaWdodD0iMTAwJSIgd2lkdGg9IjEwMCUiIHg9IjAlIiB5PSIwJSI+PGZlQ29sb3JNYXRyaXggaW49IlNvdXJjZUdyYXBoaWMiIHZhbHVlcz0iMCAwIDAgMCAxIDAgMCAwIDAgMSAwIDAgMCAwIDEgMCAwIDAgMSAwIi8+PC9maWx0ZXI+PG1hc2sgaWQ9Im0iPjxwYXRoIGQ9Im0wIDBoMTZ2MTZoLTE2eiIgZmlsbC1vcGFjaXR5PSIuOTg4IiBmaWx0ZXI9InVybCgjbCkiLz48L21hc2s+PGxpbmVhckdyYWRpZW50IGlkPSJuIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjI0OC43MDgiIHgyPSIyNDguNzA4IiB4bGluazpocmVmPSIjaiIgeTE9IjIzMy41IiB5Mj0iLTE2OC42NjciLz48Y2xpcFBhdGggaWQ9Im8iPjxwYXRoIGQ9Im0wIDBoMTZ2MTZoLTE2eiIvPjwvY2xpcFBhdGg+PG1hc2sgaWQ9InAiPjxwYXRoIGQ9Im0wIDBoMTZ2MTZoLTE2eiIgZmlsbC1vcGFjaXR5PSIuOTg4IiBmaWx0ZXI9InVybCgjbCkiLz48L21hc2s+PGNsaXBQYXRoIGlkPSJxIj48cGF0aCBkPSJtMCAwaDE2djE2aC0xNnoiLz48L2NsaXBQYXRoPjxtYXNrIGlkPSJyIj48cGF0aCBkPSJtMCAwaDE2djE2aC0xNnoiIGZpbGwtb3BhY2l0eT0iLjk4OCIgZmlsdGVyPSJ1cmwoI2wpIi8+PC9tYXNrPjxjbGlwUGF0aCBpZD0icyI+PHBhdGggZD0ibTAgMGgxNnYxNmgtMTZ6Ii8+PC9jbGlwUGF0aD48bWFzayBpZD0idCI+PHBhdGggZD0ibTAgMGgxNnYxNmgtMTZ6IiBmaWxsLW9wYWNpdHk9Ii45ODgiIGZpbHRlcj0idXJsKCNsKSIvPjwvbWFzaz48Y2xpcFBhdGggaWQ9InUiPjxwYXRoIGQ9Im0wIDBoMTZ2MTZoLTE2eiIvPjwvY2xpcFBhdGg+PG1hc2sgaWQ9InYiPjxwYXRoIGQ9Im0wIDBoMTZ2MTZoLTE2eiIgZmlsbC1vcGFjaXR5PSIuOTg4IiBmaWx0ZXI9InVybCgjbCkiLz48L21hc2s+PGNsaXBQYXRoIGlkPSJ3Ij48cGF0aCBkPSJtMCAwaDE2djE2aC0xNnoiLz48L2NsaXBQYXRoPjxtYXNrIGlkPSJ4Ij48cGF0aCBkPSJtMCAwaDE2djE2aC0xNnoiIGZpbGwtb3BhY2l0eT0iLjk4OCIgZmlsdGVyPSJ1cmwoI2wpIi8+PC9tYXNrPjxjbGlwUGF0aCBpZD0ieSI+PHBhdGggZD0ibTAgMGgxNnYxNmgtMTZ6Ii8+PC9jbGlwUGF0aD48bWFzayBpZD0ieiI+PHBhdGggZD0ibTAgMGgxNnYxNmgtMTZ6IiBmaWxsLW9wYWNpdHk9Ii45ODgiIGZpbHRlcj0idXJsKCNsKSIvPjwvbWFzaz48Y2xpcFBhdGggaWQ9IkEiPjxwYXRoIGQ9Im0wIDBoMTZ2MTZoLTE2eiIvPjwvY2xpcFBhdGg+PG1hc2sgaWQ9IkIiPjxwYXRoIGQ9Im0wIDBoMTZ2MTZoLTE2eiIgZmlsbC1vcGFjaXR5PSIuOTg4IiBmaWx0ZXI9InVybCgjbCkiLz48L21hc2s+PGNsaXBQYXRoIGlkPSJDIj48cGF0aCBkPSJtMCAwaDE2djE2aC0xNnoiLz48L2NsaXBQYXRoPjxtYXNrIGlkPSJEIj48cGF0aCBkPSJtMCAwaDE2djE2aC0xNnoiIGZpbGwtb3BhY2l0eT0iLjk4OCIgZmlsdGVyPSJ1cmwoI2wpIi8+PC9tYXNrPjxjbGlwUGF0aCBpZD0iRSI+PHBhdGggZD0ibTAgMGgxNnYxNmgtMTZ6Ii8+PC9jbGlwUGF0aD48cGF0aCBkPSJtMy45NjkgMTQuMDE2aDcuNjF2LjY2NGgtNy42MXptMCAwIiBmaWxsPSJ1cmwoI2IpIi8+PHBhdGggZD0ibTExLjU3OCAxNC4wMTZoMS4zMnYtLjY2aC0xLjMyem0wIDAiIGZpbGw9InVybCgjZCkiLz48cGF0aCBkPSJtMy45NjkgMTQuMDE2aC0xLjMyNHYuNjY0aDEuMzI0em0wIDAiIGZpbGw9InVybCgjZSkiLz48cGF0aCBkPSJtMy45NjkgMTQuMDE2aC0xLjMyNHYtLjY2aDEuMzI0em0wIDAiIGZpbGw9InVybCgjZikiLz48cGF0aCBkPSJtMTEuNTc4IDE0LjAxNmgxLjMydi42NjRoLTEuMzJ6bTAgMCIgZmlsbD0idXJsKCNnKSIvPjxwYXRoIGQ9Im05LjI3IDEuNDVoLTUuOTYxYS42Ni42NiAwIDAgMCAtLjY2NC42NnYxMS4yNDVjMCAuMzY4LjI5Ni42Ni42NjQuNjZoOC45M2MuMzY2IDAgLjY2LS4yOTIuNjYtLjY2di04LjI3N3ptMCAwIiBmaWxsPSJ1cmwoI2gpIi8+PHBhdGggZD0ibTkuMjYyIDQuNTloMy4xNGwuNDk2LjQ5NnYuNjZoLTMuNjM2em0wIDAiIGZpbGw9InVybCgjaSkiLz48cGF0aCBkPSJtMi42NDUgMTMuMDk4di4yNTdjMCAuMzY4LjI5Ni42Ni42NjQuNjZoOC45M2MuMzY2IDAgLjY2LS4yOTIuNjYtLjY2di0uMjU3YS42Ni42NiAwIDAgMSAtLjY2LjY2NGgtOC45M2EuNjY0LjY2NCAwIDAgMSAtLjY2NC0uNjY0em0wIDAiIGZpbGwtb3BhY2l0eT0iLjIzNSIvPjxwYXRoIGQ9Im0zLjMwOSAxLjQ1YS42Ni42NiAwIDAgMCAtLjY2NC42NnYuMjVjMC0uMzY4LjI5Ni0uNjY1LjY2NC0uNjY1aDUuOTZsMy42MyAzLjYzdi0uMjQ3bC0zLjYzMS0zLjYyOHptMCAwIiBmaWxsPSIjZjZmNmY2IiBmaWxsLW9wYWNpdHk9Ii41ODgiLz48cGF0aCBkPSJtMTIuODk4IDUuMDc4LTMuNjI4LTMuNjI4djIuOTY1YzAgLjM2Ny4yOTYuNjY0LjY2NC42NjR6bTAgMCIgZmlsbD0idXJsKCNrKSIvPjxwYXRoIGQ9Im05LjI3IDQuMjU4di4xNTZjMCAuMzY3LjI5Ni42NjQuNjY0LjY2NGgyLjk2NGwtLjE1Ni0uMTU2aC0yLjgwOGEuNjY0LjY2NCAwIDAgMSAtLjY2NC0uNjY0em0wIDAiIGZpbGwtb3BhY2l0eT0iLjIzNSIvPjxwYXRoIGQ9Im05LjI3IDEuNDV2LjI0NWwzLjM4MiAzLjM4M2guMjQ2em0wIDAiIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iLjM5MiIvPjxwYXRoIGQ9Im05LjI3IDEuNDAyYS4wNDcuMDQ3IDAgMCAwIC0uMDQ3LjA0N3YyLjk2NWMwIC4zOTUuMzE2LjcxMS43MS43MTFoMi45NjVjLjAyIDAgLjA0LS4wMTIuMDQzLS4wMzEuMDA4LS4wMTYuMDA0LS4wMzUtLjAwNy0uMDUxbC0zLjYzLTMuNjI5Yy0uMDA3LS4wMDgtLjAyMy0uMDEyLS4wMzQtLjAxMnptLjA0Ni4xNiAzLjQ3IDMuNDdoLTIuODUzYS42MTQuNjE0IDAgMCAxIC0uNjE4LS42MTh6bTAgMCIgZmlsbD0iIzIxMjEyMSIvPjxwYXRoIGQ9Im0zLjMwOSAxLjQwMmEuNzA4LjcwOCAwIDAgMCAtLjcxMS43MDd2MTEuMjQ2YzAgLjM5MS4zMTYuNzA3LjcxLjcwN2g4LjkzYy4zOSAwIC43MDctLjMxNi43MDctLjcwN3YtOC4yNzdjMC0uMDE2LS4wMDQtLjAyNy0uMDExLS4wMzVsLTMuNjMtMy42MjljLS4wMDctLjAwOC0uMDE5LS4wMTItLjAzNC0uMDEyem0wIC4wOTRoNS45NDFsMy42MDIgMy42MDJ2OC4yNTdjMCAuMzQtLjI3NC42MTQtLjYxNC42MTRoLTguOTNhLjYxMy42MTMgMCAwIDEgLS42MTctLjYxNHYtMTEuMjQ1YzAtLjM0LjI3NC0uNjEzLjYxOC0uNjEzem0wIDAiIGZpbGw9IiMyMTIxMjEiLz48ZyBjbGlwLXBhdGg9InVybCgjbykiIG1hc2s9InVybCgjbSkiPjxwYXRoIGQ9Im0xNjMuOTg0LTMxLjE0M2gzNy4xMjJjMi44NzUgMCA1LjI1IDIuMzc1IDUuMjUgNS4zNzV2MjYuMzcyYzAgMy0yLjM3NSA1LjM3NS01LjI1IDUuMzc1aC0zNy4xMjJjLTIuODc0IDAtNS4yNS0yLjM3NS01LjI1LTUuMzc1di0yNi4zNzJjMC0zIDIuMzc2LTUuMzc1IDUuMjUtNS4zNzV6bTAgMCIgZmlsbD0idXJsKCNuKSIgc3Ryb2tlPSIjMjEyMTIxIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAzMTI1IDAgMCAuMDMxMjUgMCA2LjcyKSIvPjwvZz48ZyBjbGlwLXBhdGg9InVybCgjcSkiIG1hc2s9InVybCgjcCkiPjxwYXRoIGQ9Im0yMzAuMjI4LTMxLjE0M2gzNi45OTZjMi44NzUgMCA1LjI1IDIuMzc1IDUuMjUgNS4zNzV2MjYuMzcyYzAgMy0yLjM3NSA1LjM3NS01LjI1IDUuMzc1aC0zNi45OTZjLTMgMC01LjM3NC0yLjM3NS01LjM3NC01LjM3NXYtMjYuMzcyYzAtMyAyLjM3NC01LjM3NSA1LjM3NC01LjM3NXptMCAwIiBmaWxsPSJ1cmwoI24pIiBzdHJva2U9IiMyMTIxMjEiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzIiB0cmFuc2Zvcm09Im1hdHJpeCguMDMxMjUgMCAwIC4wMzEyNSAwIDYuNzIpIi8+PC9nPjxnIGNsaXAtcGF0aD0idXJsKCNzKSIgbWFzaz0idXJsKCNyKSI+PHBhdGggZD0ibTI5Ni4zNDctMzEuMTQzaDM2Ljk5NmMzIDAgNS4zNzUgMi4zNzUgNS4zNzUgNS4zNzV2MjYuMzcyYzAgMy0yLjM3NSA1LjM3NS01LjM3NSA1LjM3NWgtMzYuOTk2Yy0yLjg3NSAwLTUuMjUtMi4zNzUtNS4yNS01LjM3NXYtMjYuMzcyYzAtMyAyLjM3NS01LjM3NSA1LjI1LTUuMzc1em0wIDAiIGZpbGw9InVybCgjbikiIHN0cm9rZT0iIzIxMjEyMSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjMiIHRyYW5zZm9ybT0ibWF0cml4KC4wMzEyNSAwIDAgLjAzMTI1IDAgNi43MikiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI3UpIiBtYXNrPSJ1cmwoI3QpIj48cGF0aCBkPSJtMTYzLjk4NCA4MC4wOTdoMzcuMTIyYzIuODc1IDAgNS4yNSAyLjM3NSA1LjI1IDUuMjV2MjYuNDk3YzAgMi44NzUtMi4zNzUgNS4yNS01LjI1IDUuMjVoLTM3LjEyMmMtMi44NzQgMC01LjI1LTIuMzc1LTUuMjUtNS4yNXYtMjYuNDk4YzAtMi44NzQgMi4zNzYtNS4yNSA1LjI1LTUuMjV6bTAgMCIgZmlsbD0idXJsKCNuKSIgc3Ryb2tlPSIjMjEyMTIxIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAzMTI1IDAgMCAuMDMxMjUgMCA2LjcyKSIvPjwvZz48ZyBjbGlwLXBhdGg9InVybCgjdykiIG1hc2s9InVybCgjdikiPjxwYXRoIGQ9Im0yMzAuMjI4IDgwLjA5N2gzNi45OTZjMi44NzUgMCA1LjI1IDIuMzc1IDUuMjUgNS4yNXYyNi40OTdjMCAyLjg3NS0yLjM3NSA1LjI1LTUuMjUgNS4yNWgtMzYuOTk2Yy0zIDAtNS4zNzQtMi4zNzUtNS4zNzQtNS4yNXYtMjYuNDk4YzAtMi44NzQgMi4zNzQtNS4yNSA1LjM3NC01LjI1em0wIDAiIGZpbGw9InVybCgjbikiIHN0cm9rZT0iIzIxMjEyMSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjMiIHRyYW5zZm9ybT0ibWF0cml4KC4wMzEyNSAwIDAgLjAzMTI1IDAgNi43MikiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI3kpIiBtYXNrPSJ1cmwoI3gpIj48cGF0aCBkPSJtMjk2LjM0NyA4MC4wOTdoMzYuOTk2YzMgMCA1LjM3NSAyLjM3NSA1LjM3NSA1LjI1djI2LjQ5N2MwIDIuODc1LTIuMzc1IDUuMjUtNS4zNzUgNS4yNWgtMzYuOTk2Yy0yLjg3NSAwLTUuMjUtMi4zNzUtNS4yNS01LjI1di0yNi40OThjMC0yLjg3NCAyLjM3NS01LjI1IDUuMjUtNS4yNXptMCAwIiBmaWxsPSJ1cmwoI24pIiBzdHJva2U9IiMyMTIxMjEiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzIiB0cmFuc2Zvcm09Im1hdHJpeCguMDMxMjUgMCAwIC4wMzEyNSAwIDYuNzIpIi8+PC9nPjxnIGNsaXAtcGF0aD0idXJsKCNBKSIgbWFzaz0idXJsKCN6KSI+PHBhdGggZD0ibTE2My45ODQgMjQuNDc3aDM3LjEyMmMyLjg3NSAwIDUuMjUgMi4zNzUgNS4yNSA1LjI1djI2LjQ5N2MwIDIuODc1LTIuMzc1IDUuMjUtNS4yNSA1LjI1aC0zNy4xMjJjLTIuODc0IDAtNS4yNS0yLjM3NS01LjI1LTUuMjV2LTI2LjQ5N2MwLTIuODc1IDIuMzc2LTUuMjUgNS4yNS01LjI1em0wIDAiIGZpbGw9InVybCgjbikiIHN0cm9rZT0iIzIxMjEyMSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjMiIHRyYW5zZm9ybT0ibWF0cml4KC4wMzEyNSAwIDAgLjAzMTI1IDAgNi43MikiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI0MpIiBtYXNrPSJ1cmwoI0IpIj48cGF0aCBkPSJtMjMwLjIyOCAyNC40NzdoMzYuOTk2YzIuODc1IDAgNS4yNSAyLjM3NSA1LjI1IDUuMjV2MjYuNDk3YzAgMi44NzUtMi4zNzUgNS4yNS01LjI1IDUuMjVoLTM2Ljk5NmMtMyAwLTUuMzc0LTIuMzc1LTUuMzc0LTUuMjV2LTI2LjQ5N2MwLTIuODc1IDIuMzc0LTUuMjUgNS4zNzQtNS4yNXptMCAwIiBmaWxsPSJ1cmwoI24pIiBzdHJva2U9IiMyMTIxMjEiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIzIiB0cmFuc2Zvcm09Im1hdHJpeCguMDMxMjUgMCAwIC4wMzEyNSAwIDYuNzIpIi8+PC9nPjxnIGNsaXAtcGF0aD0idXJsKCNFKSIgbWFzaz0idXJsKCNEKSI+PHBhdGggZD0ibTI5Ni4zNDcgMjQuNDc3aDM2Ljk5NmMzIDAgNS4zNzUgMi4zNzUgNS4zNzUgNS4yNXYyNi40OTdjMCAyLjg3NS0yLjM3NSA1LjI1LTUuMzc1IDUuMjVoLTM2Ljk5NmMtMi44NzUgMC01LjI1LTIuMzc1LTUuMjUtNS4yNXYtMjYuNDk3YzAtMi44NzUgMi4zNzUtNS4yNSA1LjI1LTUuMjV6bTAgMCIgZmlsbD0idXJsKCNuKSIgc3Ryb2tlPSIjMjEyMTIxIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAzMTI1IDAgMCAuMDMxMjUgMCA2LjcyKSIvPjwvZz48L3N2Zz4="); | |
| 637 | +} | |
| 638 | + | |
| 639 | +.icon-zip, .icon-tar, .icon-gz, .icon-bz2, .icon-rar, .icon-7z { | |
| 640 | + background-image: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2cHQiIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2cHQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxsaW5lYXJHcmFkaWVudCBpZD0iYSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSI3LjkzNzUiIHgyPSI3LjkzNzUiIHkxPSIxNC4wMTY4NzUiIHkyPSIxNC42NzgzNDM3NSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLW9wYWNpdHk9Ii4yNzUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJiIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3Atb3BhY2l0eT0iLjMxNCIvPjxzdG9wIG9mZnNldD0iLjIyMiIgc3RvcC1vcGFjaXR5PSIuMjc1Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9IjAiLz48L2xpbmVhckdyYWRpZW50PjxyYWRpYWxHcmFkaWVudCBpZD0iYyIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLS4wMzkwNyAtLjA3MDMyIDAgMjUuMDczIDMxLjc5OCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjciIHhsaW5rOmhyZWY9IiNiIi8+PHJhZGlhbEdyYWRpZW50IGlkPSJkIiBjeD0iNDUwLjkwOSIgY3k9IjE4OS41NzkiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMCAuMDM5MDcgLjA3MDMyIDAgLTkuMTk3IC0zLjc2NCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiByPSIyMS4xNjciIHhsaW5rOmhyZWY9IiNiIi8+PHJhZGlhbEdyYWRpZW50IGlkPSJlIiBjeD0iNDUwLjkwOSIgY3k9IjE4OS41NzkiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMCAtLjAzOTA3IC4wNzAzMiAwIC05LjE5NyAzMS43OTgpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgcj0iMjEuMTY3IiB4bGluazpocmVmPSIjYiIvPjxyYWRpYWxHcmFkaWVudCBpZD0iZiIgY3g9IjQ1MC45MDkiIGN5PSIxODkuNTc5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAgLjAzOTA3IC0uMDcwMzIgMCAyNS4wNzMgLTMuNzY0KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHI9IjIxLjE2NyIgeGxpbms6aHJlZj0iI2IiLz48bGluZWFyR3JhZGllbnQgaWQ9ImciIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoLjAzNzY3IDAgMCAuMTA4NyAtMi45MjcgMTYuODIpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjMwNi45MTciIHgyPSIzMDUuOTU1IiB5MT0iLTI1Ljc5MiIgeTI9Ii0xNDEuNDE1Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNmZmNjODAiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmUwYjIiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iaCI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZmZhNzI2Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZmZiNzRkIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImkiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoNC41NTYxNCAwIDAgMTMuMTQ2MTggLTM1My45NzMgMjAzNC4xOCkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMTY1LjQ4NSIgeDI9IjE2NS40ODUiIHhsaW5rOmhyZWY9IiNoIiB5MT0iLTI1Ljc5MiIgeTI9Ii0xNDEuNDE1Ii8+PGxpbmVhckdyYWRpZW50IGlkPSJqIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ijc2MCIgeDI9Ijc2MCIgeGxpbms6aHJlZj0iI2giIHkxPSIxNjk1LjExNyIgeTI9IjE3NS4xMTciLz48cGF0aCBkPSJtMy45NjkgMTQuMDE2aDcuOTM3di42NjRoLTcuOTM2em0wIDAiIGZpbGw9InVybCgjYSkiLz48cGF0aCBkPSJtMTEuOTA2IDE0LjAxNmgxLjMyNHYtLjY2aC0xLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2MpIi8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmgtMS4zMjR2LjY2NGgxLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2QpIi8+PHBhdGggZD0ibTMuOTY5IDE0LjAxNmgtMS4zMjR2LS42NmgxLjMyNHptMCAwIiBmaWxsPSJ1cmwoI2UpIi8+PHBhdGggZD0ibTExLjkwNiAxNC4wMTZoMS4zMjR2LjY2NGgtMS4zMjR6bTAgMCIgZmlsbD0idXJsKCNmKSIvPjxwYXRoIGQ9Im0zLjMwOSAxLjQ1aDkuMjYxYy4zNjQgMCAuNjYuMjk2LjY2LjY2djExLjI0NWEuNjYuNjYgMCAwIDEgLS42Ni42NmgtOS4yNmEuNjYuNjYgMCAwIDEgLS42NjQtLjY2di0xMS4yNDVjMC0uMzYzLjI5Ni0uNjYuNjY0LS42NnptMCAwIiBmaWxsPSJ1cmwoI2cpIi8+PHBhdGggZD0ibTQwMC4xMiAxNzUuMjU5Yy00NC40MDYgMC04MC4zMDggMzUuNDMtODAuMzA4IDc5LjgzNXYxMzYwLjAyNmMwIDQ0LjQwNiAzNS45MDIgNzkuODM1IDgwLjMwNyA3OS44MzVoMTE5Ljk4OXYtMTUxOS42OTV6bTAgMCIgZmlsbD0idXJsKCNpKSIgc3Ryb2tlPSIjMjEyMTIxIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS13aWR0aD0iMTEuMzM5IiB0cmFuc2Zvcm09Im1hdHJpeCguMDA4MjY5MDIgLjAwMDEyNzMgLS4wMDAxMjczIC4wMDgyNjkwMiAwIDApIi8+PHBhdGggZD0ibTMuMzA5IDEzLjk4YS42NjQuNjY0IDAgMCAxIC0uNjY0LS42NjR2LS4wODJjMCAuMzY4LjI5Ni42NjQuNjY0LjY2NGg5LjI2MWMuMzY0IDAgLjY2LS4yOTYuNjYtLjY2NHYuMDgyYS42NjMuNjYzIDAgMCAxIC0uNjYuNjY0em0wIDAiIGZpbGwtb3BhY2l0eT0iLjE5NiIvPjxwYXRoIGQ9Im02NzkuNzc4IDE3NS4yNTl2OTU5LjkwOGM1OS45OTQtMTYwLjE0MiAxMDAuMTQ4LTE2MC4xNDIgMTYwLjE0MiAwdi05NTkuOTA3em0wIDAiIGZpbGw9InVybCgjaikiIHN0cm9rZT0iIzIxMjEyMSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjExLjMzOSIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwODI2OTAyIC4wMDAxMjczIC0uMDAwMTI3MyAuMDA4MjY5MDIgMCAwKSIvPjxwYXRoIGQ9Im0zLjMwOSAxLjQ4NGEuNjY0LjY2NCAwIDAgMCAtLjY2NC42NjR2LjA4MmMwLS4zNjcuMjk2LS42NjQuNjY0LS42NjRoOS4yNjFjLjM2NCAwIC42Ni4yOTcuNjYuNjY0di0uMDgyYS42NjMuNjYzIDAgMCAwIC0uNjYtLjY2NHptMCAwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii4zOTIiLz48cGF0aCBkPSJtMy4zMDkgMS40MDJhLjcwOC43MDggMCAwIDAgLS43MTEuNzA3djExLjI0NmMwIC4zOTEuMzE2LjcwNy43MS43MDdoOS4yNjJjLjM5IDAgLjcwNy0uMzE2LjcwNy0uNzA3di0xMS4yNDVhLjcwNy43MDcgMCAwIDAgLS43MDctLjcwN3ptMCAuMDk0aDkuMjYxYy4zNCAwIC42MTQuMjc0LjYxNC42MTN2MTEuMjQ2YzAgLjM0LS4yNzQuNjE0LS42MTQuNjE0aC05LjI2YS42MTMuNjEzIDAgMCAxIC0uNjE4LS42MTR2LTExLjI0NWMwLS4zNC4yNzQtLjYxMy42MTgtLjYxM3ptMCAwIiBmaWxsPSIjMjEyMTIxIi8+PC9zdmc+"); | |
| 641 | +} | |
| 642 | + | |
| 643 | +.icon-dl { | |
| 644 | + background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPg0KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGQ9Ik0yMCAxNVYxOEMyMCAxOS4xMDQ2IDE5LjEwNDYgMjAgMTggMjBINkM0Ljg5NTQzIDIwIDQgMTkuMTA0NiA0IDE4TDQgMTVNOCAxMUwxMiAxNU0xMiAxNUwxNiAxMU0xMiAxNVYzIiBzdHJva2U9IiNmZmYiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4NCjwvc3ZnPg=="); | |
| 645 | +} | |
| @@ -0,0 +1,146 @@ | ||
| 1 | +<!doctype html> | |
| 2 | +<html> | |
| 3 | +<head> | |
| 4 | + <meta charset="utf-8"> | |
| 5 | + <meta name="theme-color" content="#fafafa"> | |
| 6 | + <meta name="msapplication-navbutton-color" content="#fafafa"> | |
| 7 | + <meta name="mobile-web-app-capable" content="yes"> | |
| 8 | + <meta name="viewport" content="width=device-width"> | |
| 9 | + | |
| 10 | + <link rel="manifest" href='data:application/manifest+json,{"name":"{{.Title}}","short_name":"{{.Title}}","description":" ","icons":[{"src":"data:image/svg+xml;base64,favicon_will_be_here","sizes":"150x150","type":"image/svg+xml"}],"background":"#fafafa","theme_color":"#fafafa","display":"standalone"}' /> | |
| 11 | + | |
| 12 | + <title>{{.Title}}</title> | |
| 13 | + <link href="data:image/svg+xml;base64,favicon_will_be_here" rel="icon" type="image/svg+xml" /> | |
| 14 | + <style type="text/css">css_will_be_here</style> | |
| 15 | + <script> | |
| 16 | + window.ro = {{.Ro}} | |
| 17 | + window.extraPath = {{.ExtraPath}}.slice(0, -1) | |
| 18 | + window.onload = function () { js_will_be_here } | |
| 19 | + </script> | |
| 20 | +</head> | |
| 21 | + | |
| 22 | +<body> | |
| 23 | + <!-- Overlays (help, sums, media, etc.) --> | |
| 24 | + <div onclick="window.helpOff()" style="display: none;" id="help"><table id="helpTable"><tbody> | |
| 25 | + <tr><td>Arrows/Enter</td><td>browse files/folders and pictures</td></tr> | |
| 26 | + <tr><td>Ctrl/Meta + Enter</td><td>download selected item as archive</td></tr> | |
| 27 | + <tr><td>Ctrl/Meta + C</td><td>copy URL to clipboard</td></tr> | |
| 28 | + <tr><td>Ctrl/Meta + E</td><td>rename item</td></tr> | |
| 29 | + <tr><td>Ctrl/Meta + Backspace</td><td>delete item</td></tr> | |
| 30 | + <tr><td>Ctrl/Meta/Shift + U</td><td>upload new file/folder</td></tr> | |
| 31 | + <tr><td>Ctrl/Meta + M</td><td>create a new directory</td></tr> | |
| 32 | + <tr><td>Ctrl/Meta + X</td><td>cut selected path</td></tr> | |
| 33 | + <tr><td>Ctrl/Meta + V</td><td>paste previously selected paths to directory</td></tr> | |
| 34 | + <tr><td>Ctrl/Meta + Z</td><td>copy checksums of selected file</td></tr> | |
| 35 | + <tr><td>Ctrl + click</td><td>download selected item as archive</td></tr> | |
| 36 | + <tr><td>click file icon </td><td>rename item</td></tr> | |
| 37 | + <tr><td>double click file icon</td><td>delete item</td></tr> | |
| 38 | + <tr><td>drag-and-drop item on UI</td><td>move item</td></tr> | |
| 39 | + <tr><td>drag-and-drop external item</td><td>upload file/folders</td></tr> | |
| 40 | + <tr><td>any other letter</td><td>fuzzy search</td></tr> | |
| 41 | + </tbody></table></div> | |
| 42 | + | |
| 43 | + <div onclick="window.sumsOff()" style="display: none;" id="sums"><table id="sumsTable"><tbody> | |
| 44 | + <tr><td>Key</td><td>Hash Algorithm</td></tr> | |
| 45 | + <tr><td>1</td><td>copy sha1 sum</td></tr> | |
| 46 | + <tr><td>2</td><td>copy sha256 sum</td></tr> | |
| 47 | + <tr><td>3</td><td>copy sha512 sum</td></tr> | |
| 48 | + <tr><td>5</td><td>copy md5 sum</td></tr> | |
| 49 | + </tbody></table></div> | |
| 50 | + | |
| 51 | + <div style="display: none;" onclick="window.quitAll()" id="quitAll"><i style="display: none;" id="toast">cant reach server</i></div> | |
| 52 | + <textarea style="display: none;" id="text-editor"></textarea> | |
| 53 | + <div id="drop-grid" style="display:none !important"></div> | |
| 54 | + <input type="file" id="clickupload" multiple style="display:none"/> | |
| 55 | + | |
| 56 | + <div id="pics" style="display:none;"> <img draggable="false" onclick="window.picsNav()" id="picsHolder" /></div> | |
| 57 | + <div id="video" style="display:none;"> | |
| 58 | + <div onclick="window.videodl()" class="icon-dl" id="video-dl"></div> | |
| 59 | + <video controls id="videoHolder"> </video> | |
| 60 | + </div> | |
| 61 | + <div id="pdf" style="display:none;"> </div> | |
| 62 | + | |
| 63 | + <!-- Theme toggle (init before DOM to prevent flash) --> | |
| 64 | + <script> | |
| 65 | + (function(){ | |
| 66 | + var t = localStorage.getItem('wormhole-theme'); | |
| 67 | + if (!t) t = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; | |
| 68 | + document.documentElement.setAttribute('data-theme', t); | |
| 69 | + window.toggleTheme = function() { | |
| 70 | + var c = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'; | |
| 71 | + document.documentElement.setAttribute('data-theme', c); | |
| 72 | + localStorage.setItem('wormhole-theme', c); | |
| 73 | + updateToggle(); | |
| 74 | + }; | |
| 75 | + window.updateToggle = function() { | |
| 76 | + var sun = document.getElementById('icon-sun'); | |
| 77 | + var moon = document.getElementById('icon-moon'); | |
| 78 | + if (!sun || !moon) return; | |
| 79 | + var dark = document.documentElement.getAttribute('data-theme') === 'dark'; | |
| 80 | + sun.style.display = dark ? 'block' : 'none'; | |
| 81 | + moon.style.display = dark ? 'none' : 'block'; | |
| 82 | + }; | |
| 83 | + document.addEventListener('DOMContentLoaded', updateToggle); | |
| 84 | + })(); | |
| 85 | + </script> | |
| 86 | + | |
| 87 | + <!-- Page container --> | |
| 88 | + <article id="page"> | |
| 89 | + | |
| 90 | + <!-- Header row: site label + path + actions --> | |
| 91 | + <div class="row row-header"> | |
| 92 | + <div class="cell cell-label">CDN</div> | |
| 93 | + <div class="cell cell-path" onclick="return titleClick(event)"><h1>.{{.Title}}</h1></div> | |
| 94 | + <div class="cell cell-actions"> | |
| 95 | + <button id="themeToggle" onclick="toggleTheme()" title="Toggle theme"> | |
| 96 | + <svg id="icon-moon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg> | |
| 97 | + <svg id="icon-sun" style="display:none" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg> | |
| 98 | + </button> | |
| 99 | + </div> | |
| 100 | + </div> | |
| 101 | + | |
| 102 | + <!-- Column headers --> | |
| 103 | + <div class="row row-colheader"> | |
| 104 | + <div class="cell cell-col-icon"></div> | |
| 105 | + <div class="cell cell-col-size">SIZE</div> | |
| 106 | + <div class="cell cell-col-name">NAME</div> | |
| 107 | + </div> | |
| 108 | + | |
| 109 | + <!-- File listing --> | |
| 110 | + <div id="listing"> | |
| 111 | + <table id="linkTable"> | |
| 112 | + {{range .RowsFolders}} | |
| 113 | + <tr> | |
| 114 | + <td class="iconRow"><i ondblclick="return rm(event)" onclick="return rename(event)" class="btn icon icon-{{.Ext}} icon-blank"></i></td> | |
| 115 | + <td class="file-size"><code>{{.Size}}</code></td> | |
| 116 | + <td class="arrow"><div class="arrow-icon"></div></td> | |
| 117 | + <td class="display-name"><a class="list-links" oncontextmenu="return setCursorTo(event.target.innerText)" onclick="return onClickLink(event)" href="{{.Href}}">{{.Name}}</a></td> | |
| 118 | + </tr> | |
| 119 | + {{end}} | |
| 120 | + {{range .RowsFiles}} | |
| 121 | + <tr> | |
| 122 | + <td class="iconRow"><i ondblclick="return rm(event)" onclick="return rename(event)" class="btn icon icon-{{.Ext}} icon-blank"></i></td> | |
| 123 | + <td class="file-size"><code>{{.Size}}</code></td> | |
| 124 | + <td class="arrow"><div class="arrow-icon"></div></td> | |
| 125 | + <td class="display-name"><a class="list-links" oncontextmenu="return setCursorTo(event.target.innerText)" onclick="return onClickLink(event)" href="{{.Href}}">{{.Name}}</a></td> | |
| 126 | + </tr> | |
| 127 | + {{end}} | |
| 128 | + </table> | |
| 129 | + </div> | |
| 130 | + | |
| 131 | + <!-- Footer row --> | |
| 132 | + <div class="row row-footer"> | |
| 133 | + <div class="cell cell-footer-help">Ctrl/Cmd + h</div> | |
| 134 | + <div class="cell cell-footer-brand">cdn.krisyotam.com</div> | |
| 135 | + </div> | |
| 136 | + | |
| 137 | + </article> | |
| 138 | + | |
| 139 | +</body> | |
| 140 | +<div id="upBar" class="bar"> | |
| 141 | + <span style="display: none;" class="barName" id="upBarName"></span> | |
| 142 | + <div style="display: none;" class="barPc" id="upBarPc">1%</div> | |
| 143 | +</div> | |
| 144 | +<div id="ok" class="notif icon-large-ok"></div> | |
| 145 | +<div id="sad" class="notif icon-large-sad-server"></div> | |
| 146 | +</html> | |
| @@ -0,0 +1,318 @@ | ||
| 1 | +package main | |
| 2 | + | |
| 3 | +import ( | |
| 4 | + "archive/zip" | |
| 5 | + "compress/gzip" | |
| 6 | + "crypto/md5" | |
| 7 | + "crypto/sha1" | |
| 8 | + "crypto/sha256" | |
| 9 | + "crypto/sha512" | |
| 10 | + _ "embed" | |
| 11 | + "encoding/hex" | |
| 12 | + "encoding/json" | |
| 13 | + "errors" | |
| 14 | + "flag" | |
| 15 | + "fmt" | |
| 16 | + "hash" | |
| 17 | + "html" | |
| 18 | + "html/template" | |
| 19 | + "io" | |
| 20 | + "io/fs" | |
| 21 | + "log" | |
| 22 | + "net/http" | |
| 23 | + "net/url" | |
| 24 | + "os" | |
| 25 | + "path/filepath" | |
| 26 | + "sort" | |
| 27 | + "strconv" | |
| 28 | + "strings" | |
| 29 | +) | |
| 30 | + | |
| 31 | +type rowTemplate struct { | |
| 32 | + Name string | |
| 33 | + Href template.URL | |
| 34 | + Size string | |
| 35 | + Ext string | |
| 36 | +} | |
| 37 | + | |
| 38 | +type pageTemplate struct { | |
| 39 | + Title template.HTML | |
| 40 | + ExtraPath template.HTML | |
| 41 | + Ro bool | |
| 42 | + RowsFiles []rowTemplate | |
| 43 | + RowsFolders []rowTemplate | |
| 44 | +} | |
| 45 | + | |
| 46 | +var host = flag.String("h", "127.0.0.1", "host to listen to") | |
| 47 | +var port = flag.String("p", "8001", "port to listen to") | |
| 48 | +var extraPath = flag.String("prefix", "/", "url prefix at which wormhole can be reached, e.g. /wormhole/ (slashes of importance)") | |
| 49 | +var symlinks = flag.Bool("symlinks", false, "follow symlinks \033[4mWARNING\033[0m: symlinks will by nature allow to escape the defined path (default: false)") | |
| 50 | +var verb = flag.Bool("verb", false, "verbosity") | |
| 51 | +var skipHidden = flag.Bool("k", true, "\nskip hidden files") | |
| 52 | +var ro = flag.Bool("ro", false, "read only mode (no upload, rename, move, etc...)") | |
| 53 | + | |
| 54 | +type rpcCall struct { | |
| 55 | + Call string `json:"call"` | |
| 56 | + Args []string `json:"args"` | |
| 57 | +} | |
| 58 | + | |
| 59 | +var rootPath = "" | |
| 60 | +var handler http.Handler | |
| 61 | + | |
| 62 | +func check(e error) { | |
| 63 | + if e != nil { | |
| 64 | + panic(e) | |
| 65 | + } | |
| 66 | +} | |
| 67 | + | |
| 68 | +func exitPath(w http.ResponseWriter, s ...interface{}) { | |
| 69 | + if r := recover(); r != nil { | |
| 70 | + log.Println("error", s, r) | |
| 71 | + w.WriteHeader(500) | |
| 72 | + w.Write([]byte("error")) | |
| 73 | + } else if *verb { | |
| 74 | + log.Println(s...) | |
| 75 | + } | |
| 76 | +} | |
| 77 | + | |
| 78 | +func humanize(bytes int64) string { | |
| 79 | + b := float64(bytes) | |
| 80 | + u := 0 | |
| 81 | + for { | |
| 82 | + if b < 1024 { | |
| 83 | + return strconv.FormatFloat(b, 'f', 1, 64) + [9]string{"B", "k", "M", "G", "T", "P", "E", "Z", "Y"}[u] | |
| 84 | + } | |
| 85 | + b = b / 1024 | |
| 86 | + u++ | |
| 87 | + } | |
| 88 | +} | |
| 89 | + | |
| 90 | +func replyList(w http.ResponseWriter, r *http.Request, fullPath string, path string) { | |
| 91 | + files, err := os.ReadDir(fullPath) | |
| 92 | + check(err) | |
| 93 | + sort.Slice(files, func(i, j int) bool { return strings.ToLower(files[i].Name()) < strings.ToLower(files[j].Name()) }) | |
| 94 | + | |
| 95 | + if !strings.HasSuffix(path, "/") { | |
| 96 | + path += "/" | |
| 97 | + } | |
| 98 | + | |
| 99 | + title := "/" + strings.TrimPrefix(path, *extraPath) | |
| 100 | + p := pageTemplate{} | |
| 101 | + if path != *extraPath { | |
| 102 | + p.RowsFolders = append(p.RowsFolders, rowTemplate{"../", "../", "", "folder"}) | |
| 103 | + } | |
| 104 | + p.ExtraPath = template.HTML(html.EscapeString(*extraPath)) | |
| 105 | + p.Ro = *ro | |
| 106 | + p.Title = template.HTML(html.EscapeString(title)) | |
| 107 | + | |
| 108 | + for _, el := range files { | |
| 109 | + info, errInfo := el.Info() | |
| 110 | + el, err := os.Stat(fullPath + "/" + el.Name()) | |
| 111 | + if err != nil || errInfo != nil { | |
| 112 | + log.Println("error - cant stat a file", err) | |
| 113 | + continue | |
| 114 | + } | |
| 115 | + | |
| 116 | + if *skipHidden && strings.HasPrefix(el.Name(), ".") { | |
| 117 | + continue // dont print hidden files if we're not allowed | |
| 118 | + } | |
| 119 | + if !*symlinks && info.Mode()&os.ModeSymlink != 0 { | |
| 120 | + continue // dont follow symlinks if we're not allowed | |
| 121 | + } | |
| 122 | + | |
| 123 | + href := url.PathEscape(el.Name()) | |
| 124 | + name := el.Name() | |
| 125 | + | |
| 126 | + if el.IsDir() && strings.HasPrefix(href, "/") { | |
| 127 | + href = strings.Replace(href, "/", "", 1) | |
| 128 | + } | |
| 129 | + | |
| 130 | + if el.IsDir() { | |
| 131 | + row := rowTemplate{name + "/", template.URL(href), "", "folder"} | |
| 132 | + p.RowsFolders = append(p.RowsFolders, row) | |
| 133 | + } else { | |
| 134 | + sl := strings.Split(name, ".") | |
| 135 | + ext := strings.ToLower(sl[len(sl)-1]) | |
| 136 | + row := rowTemplate{name, template.URL(href), humanize(el.Size()), ext} | |
| 137 | + p.RowsFiles = append(p.RowsFiles, row) | |
| 138 | + } | |
| 139 | + } | |
| 140 | + | |
| 141 | + if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { | |
| 142 | + w.Header().Set("Content-Type", "text/html") | |
| 143 | + w.Header().Add("Content-Encoding", "gzip") | |
| 144 | + gz, err := gzip.NewWriterLevel(w, gzip.BestSpeed) // BestSpeed is Much Faster than default - base on a very unscientific local test, and only ~30% larger (compression remains still very effective, ~6x) | |
| 145 | + check(err) | |
| 146 | + defer gz.Close() | |
| 147 | + tmpl.Execute(gz, p) | |
| 148 | + } else { | |
| 149 | + tmpl.Execute(w, p) | |
| 150 | + } | |
| 151 | +} | |
| 152 | + | |
| 153 | +func doContent(w http.ResponseWriter, r *http.Request) { | |
| 154 | + if !strings.HasPrefix(r.URL.Path, *extraPath) { // redir when were not hitting the supplementary path if one is set | |
| 155 | + http.Redirect(w, r, *extraPath, http.StatusFound) | |
| 156 | + return | |
| 157 | + } | |
| 158 | + | |
| 159 | + path := html.UnescapeString(r.URL.Path) | |
| 160 | + defer exitPath(w, "get content", path) | |
| 161 | + fullPath := enforcePath(path) | |
| 162 | + stat, errStat := os.Stat(fullPath) | |
| 163 | + check(errStat) | |
| 164 | + | |
| 165 | + if stat.IsDir() { | |
| 166 | + replyList(w, r, fullPath, path) | |
| 167 | + } else { | |
| 168 | + handler.ServeHTTP(w, r) | |
| 169 | + } | |
| 170 | +} | |
| 171 | + | |
| 172 | +func upload(w http.ResponseWriter, r *http.Request) { | |
| 173 | + path := r.Header.Get("wormhole-path") | |
| 174 | + defer exitPath(w, "upload", path) | |
| 175 | + | |
| 176 | + path, err := url.PathUnescape(path) | |
| 177 | + check(err) | |
| 178 | + reader, err := r.MultipartReader() | |
| 179 | + check(err) | |
| 180 | + part, err := reader.NextPart() | |
| 181 | + if err != nil && err != io.EOF { // errs EOF when no more parts to process | |
| 182 | + check(err) | |
| 183 | + } | |
| 184 | + dst, err := os.Create(enforcePath(path)) | |
| 185 | + check(err) | |
| 186 | + io.Copy(dst, part) | |
| 187 | + w.Write([]byte("ok")) | |
| 188 | +} | |
| 189 | + | |
| 190 | +func zipRPC(w http.ResponseWriter, r *http.Request) { | |
| 191 | + zipPath := r.URL.Query().Get("zipPath") | |
| 192 | + zipName := r.URL.Query().Get("zipName") | |
| 193 | + defer exitPath(w, "zip", zipPath) | |
| 194 | + zipFullPath := enforcePath(zipPath) | |
| 195 | + _, err := os.Lstat(zipFullPath) | |
| 196 | + check(err) | |
| 197 | + w.Header().Add("Content-Disposition", "attachment; filename=\""+zipName+".zip\"") | |
| 198 | + zipWriter := zip.NewWriter(w) | |
| 199 | + defer zipWriter.Close() | |
| 200 | + | |
| 201 | + err = filepath.Walk(zipFullPath, func(path string, f fs.FileInfo, err error) error { | |
| 202 | + check(err) | |
| 203 | + if f.IsDir() { | |
| 204 | + return nil | |
| 205 | + } | |
| 206 | + | |
| 207 | + rel, err := filepath.Rel(zipFullPath, path) | |
| 208 | + check(err) | |
| 209 | + if *skipHidden && (strings.HasPrefix(rel, ".") || strings.HasPrefix(f.Name(), ".")) { | |
| 210 | + return nil // hidden files not allowed | |
| 211 | + } | |
| 212 | + if f.Mode()&os.ModeSymlink != 0 { | |
| 213 | + panic(errors.New("symlink not allowed in zip downloads")) // filepath.Walk doesnt support symlinks | |
| 214 | + } | |
| 215 | + | |
| 216 | + header, err := zip.FileInfoHeader(f) | |
| 217 | + check(err) | |
| 218 | + header.Name = filepath.ToSlash(rel) // make the paths consistent between OSes | |
| 219 | + header.Method = zip.Store | |
| 220 | + headerWriter, err := zipWriter.CreateHeader(header) | |
| 221 | + check(err) | |
| 222 | + file, err := os.Open(path) | |
| 223 | + check(err) | |
| 224 | + defer file.Close() | |
| 225 | + _, err = io.Copy(headerWriter, file) | |
| 226 | + check(err) | |
| 227 | + return nil | |
| 228 | + }) | |
| 229 | + | |
| 230 | + check(err) | |
| 231 | +} | |
| 232 | + | |
| 233 | +func rpc(w http.ResponseWriter, r *http.Request) { | |
| 234 | + var err error | |
| 235 | + var rpc rpcCall | |
| 236 | + defer exitPath(w, "rpc", &rpc) | |
| 237 | + bodyBytes, err := io.ReadAll(r.Body) | |
| 238 | + check(err) | |
| 239 | + json.Unmarshal(bodyBytes, &rpc) | |
| 240 | + ret := []byte("ok") | |
| 241 | + | |
| 242 | + switch rpc.Call { | |
| 243 | + case "mkdirp": | |
| 244 | + err = os.MkdirAll(enforcePath(rpc.Args[0]), os.ModePerm) | |
| 245 | + case "mv": | |
| 246 | + err = os.Rename(enforcePath(rpc.Args[0]), enforcePath(rpc.Args[1])) | |
| 247 | + case "rm": | |
| 248 | + err = os.RemoveAll(enforcePath(rpc.Args[0])) | |
| 249 | + case "sum": | |
| 250 | + file, err := os.Open(enforcePath(rpc.Args[0])) | |
| 251 | + check(err) | |
| 252 | + var hash hash.Hash | |
| 253 | + switch rpc.Args[1] { | |
| 254 | + case "md5": | |
| 255 | + hash = md5.New() | |
| 256 | + case "sha1": | |
| 257 | + hash = sha1.New() | |
| 258 | + case "sha256": | |
| 259 | + hash = sha256.New() | |
| 260 | + case "sha512": | |
| 261 | + hash = sha512.New() | |
| 262 | + } | |
| 263 | + _, err = io.Copy(hash, file) | |
| 264 | + check(err) | |
| 265 | + checksum := hash.Sum(nil) | |
| 266 | + ret = make([]byte, hex.EncodedLen(len(checksum))) | |
| 267 | + hex.Encode(ret, checksum) | |
| 268 | + } | |
| 269 | + | |
| 270 | + check(err) | |
| 271 | + w.Write(ret) | |
| 272 | +} | |
| 273 | + | |
| 274 | +func enforcePath(p string) string { | |
| 275 | + joined := filepath.Join(rootPath, strings.TrimPrefix(p, *extraPath)) | |
| 276 | + fp, err := filepath.Abs(joined) | |
| 277 | + sl, _ := filepath.EvalSymlinks(fp) // err skipped as it would error for unexistent files (RPC check). The actual behaviour is tested below | |
| 278 | + | |
| 279 | + // panic if we had a error getting absolute path, | |
| 280 | + // ... or if path doesnt contain the prefix path we expect, | |
| 281 | + // ... or if we're skipping hidden folders, and one is requested, | |
| 282 | + // ... or if we're skipping symlinks, path exists, and a symlink out of bound requested | |
| 283 | + if err != nil || !strings.HasPrefix(fp, rootPath) || *skipHidden && strings.Contains(p, "/.") || !*symlinks && len(sl) > 0 && !strings.HasPrefix(sl, rootPath) { | |
| 284 | + panic(errors.New("invalid path")) | |
| 285 | + } | |
| 286 | + | |
| 287 | + return fp | |
| 288 | +} | |
| 289 | + | |
| 290 | +func main() { | |
| 291 | + if flag.Parse(); len(flag.Args()) == 1 { | |
| 292 | + rootPath = flag.Args()[0] | |
| 293 | + } else { | |
| 294 | + fmt.Printf("\nusage: ./wormhole [OPTIONS] ~/directory-to-share\n\n") | |
| 295 | + flag.PrintDefaults() | |
| 296 | + os.Exit(1) | |
| 297 | + } | |
| 298 | + | |
| 299 | + var err error | |
| 300 | + rootPath, err = filepath.Abs(rootPath) | |
| 301 | + check(err) | |
| 302 | + server := &http.Server{Addr: *host + ":" + *port, Handler: handler} | |
| 303 | + | |
| 304 | + if !*ro { | |
| 305 | + http.HandleFunc(*extraPath+"rpc", rpc) | |
| 306 | + http.HandleFunc(*extraPath+"post", upload) | |
| 307 | + } | |
| 308 | + http.HandleFunc(*extraPath+"zip", zipRPC) | |
| 309 | + http.HandleFunc("/", doContent) | |
| 310 | + handler = http.StripPrefix(*extraPath, http.FileServer(http.Dir(rootPath))) | |
| 311 | + | |
| 312 | + fmt.Printf("Wormhole starting on directory %s\n", rootPath) | |
| 313 | + fmt.Printf("Verbose: %t, Symlinks: %t, Read-Only: %t, Hidden-Files Skipped: %t\n", *verb, *symlinks, *ro, *skipHidden) | |
| 314 | + fmt.Printf("Listening on http://%s:%s%s\n", *host, *port, *extraPath) | |
| 315 | + if err = server.ListenAndServe(); err != http.ErrServerClosed { | |
| 316 | + check(err) | |
| 317 | + } | |
| 318 | +} | |
| @@ -0,0 +1,34 @@ | ||
| 1 | +package main | |
| 2 | + | |
| 3 | +import ( | |
| 4 | + _ "embed" | |
| 5 | + "encoding/base64" | |
| 6 | + "html/template" | |
| 7 | + "strings" | |
| 8 | +) | |
| 9 | + | |
| 10 | +//go:embed ui/script.js | |
| 11 | +var scriptJs string | |
| 12 | + | |
| 13 | +//go:embed ui/style.css | |
| 14 | +var styleCss string | |
| 15 | + | |
| 16 | +//go:embed ui/favicon.svg | |
| 17 | +var faviconSvg []byte | |
| 18 | + | |
| 19 | +//go:embed ui/ui.tmpl | |
| 20 | +var uiTmpl string | |
| 21 | + | |
| 22 | +var tmpl *template.Template | |
| 23 | + | |
| 24 | +// fill in template | |
| 25 | +func init() { | |
| 26 | + var err error | |
| 27 | + t := strings.Replace(uiTmpl, "css_will_be_here", styleCss, 1) | |
| 28 | + t = strings.Replace(t, "js_will_be_here", scriptJs, 1) | |
| 29 | + t = strings.Replace(t, "favicon_will_be_here", base64.StdEncoding.EncodeToString(faviconSvg), 2) | |
| 30 | + tmpl, err = template.New("").Parse(t) | |
| 31 | + if err != nil { | |
| 32 | + panic(err) | |
| 33 | + } | |
| 34 | +} | |
| @@ -0,0 +1,390 @@ | ||
| 1 | +package main | |
| 2 | + | |
| 3 | +import ( | |
| 4 | + "archive/zip" | |
| 5 | + "bytes" | |
| 6 | + "fmt" | |
| 7 | + "io/ioutil" | |
| 8 | + "net/http" | |
| 9 | + "regexp" | |
| 10 | + "strings" | |
| 11 | + "testing" | |
| 12 | +) | |
| 13 | + | |
| 14 | +func dieMaybe(t *testing.T, err error) { | |
| 15 | + if err != nil { | |
| 16 | + t.Fatal(err) | |
| 17 | + } | |
| 18 | +} | |
| 19 | + | |
| 20 | +func trimSpaces(str string) string { | |
| 21 | + space := regexp.MustCompile(`\s+`) | |
| 22 | + return space.ReplaceAllString(str, " ") | |
| 23 | +} | |
| 24 | + | |
| 25 | +func getRaw(t *testing.T, url string) []byte { | |
| 26 | + resp, err := http.Get(url) | |
| 27 | + dieMaybe(t, err) | |
| 28 | + body, err := ioutil.ReadAll(resp.Body) | |
| 29 | + dieMaybe(t, err) | |
| 30 | + return body | |
| 31 | +} | |
| 32 | + | |
| 33 | +func getZip(t *testing.T, needle string, dest string) (int, bool) { | |
| 34 | + b := getRaw(t, dest) | |
| 35 | + unzipped, err := zip.NewReader(bytes.NewReader(b), int64(len(b))) | |
| 36 | + dieMaybe(t, err) | |
| 37 | + length := len(unzipped.File) | |
| 38 | + for _, file := range unzipped.File { | |
| 39 | + if file.Name == needle { | |
| 40 | + return length, true | |
| 41 | + } | |
| 42 | + } | |
| 43 | + return length, false | |
| 44 | +} | |
| 45 | + | |
| 46 | +func get(t *testing.T, url string) string { | |
| 47 | + body := getRaw(t, url) | |
| 48 | + return trimSpaces(string(body)) | |
| 49 | +} | |
| 50 | + | |
| 51 | +func postDummyFile(t *testing.T, url string, path string, payload string) string { | |
| 52 | + // Generated by curl-to-Go: https://mholt.github.io/curl-to-go | |
| 53 | + body := strings.NewReader("------WebKitFormBoundarycCRIderiXxJWEUcU\r\nContent-Disposition: form-data; name=\"\u1112\u1161 \u1112\u1161\"; filename=\"\u1112\u1161 \u1112\u1161\"\r\nContent-Type: application/octet-stream\r\n\r\n" + payload) | |
| 54 | + req, err := http.NewRequest("POST", url+"post", body) | |
| 55 | + dieMaybe(t, err) | |
| 56 | + req.Header.Set("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundarycCRIderiXxJWEUcU") | |
| 57 | + req.Header.Set("Wormhole-Path", path) | |
| 58 | + | |
| 59 | + resp, err := http.DefaultClient.Do(req) | |
| 60 | + dieMaybe(t, err) | |
| 61 | + defer resp.Body.Close() | |
| 62 | + bodyS, err := ioutil.ReadAll(resp.Body) | |
| 63 | + dieMaybe(t, err) | |
| 64 | + return trimSpaces(string(bodyS)) | |
| 65 | +} | |
| 66 | + | |
| 67 | +func postJSON(t *testing.T, url string, what string) string { | |
| 68 | + resp, err := http.Post(url, "application/json", bytes.NewBuffer([]byte(what))) | |
| 69 | + dieMaybe(t, err) | |
| 70 | + body, err := ioutil.ReadAll(resp.Body) | |
| 71 | + dieMaybe(t, err) | |
| 72 | + return trimSpaces(string(body)) | |
| 73 | +} | |
| 74 | + | |
| 75 | +func fetchAndTestDefault(t *testing.T, url string) string { | |
| 76 | + body0 := get(t, url) | |
| 77 | + | |
| 78 | + if !strings.Contains(body0, `<title>/</title>`) { | |
| 79 | + t.Fatal("error title") | |
| 80 | + } | |
| 81 | + | |
| 82 | + if !strings.Contains(body0, `<h1 onclick="return titleClick(event)">./</h1>`) { | |
| 83 | + t.Fatal("error header") | |
| 84 | + } | |
| 85 | + | |
| 86 | + if !strings.Contains(body0, `href="hols">hols/</a>`) { | |
| 87 | + t.Fatal("error hols folder") | |
| 88 | + } | |
| 89 | + | |
| 90 | + if !strings.Contains(body0, `href="curimit@gmail.com%20%2840%25%29">curimit@gmail.com (40%)/</a>`) { | |
| 91 | + t.Fatal("error curimit@gmail.com (40%) folder") | |
| 92 | + } | |
| 93 | + | |
| 94 | + if !strings.Contains(body0, `href="%E4%B8%AD%E6%96%87">中文/</a>`) { | |
| 95 | + t.Fatal("error 中文 folder") | |
| 96 | + } | |
| 97 | + | |
| 98 | + if !strings.Contains(body0, `href="custom_mime_type.types">custom_mime_type.types</a>`) { | |
| 99 | + t.Fatal("error row custom_mime_type") | |
| 100 | + } | |
| 101 | + | |
| 102 | + return body0 | |
| 103 | +} | |
| 104 | + | |
| 105 | +func doTestRegular(t *testing.T, url string, testExtra bool) { | |
| 106 | + var payload, path, body0, body1, body2 string | |
| 107 | + | |
| 108 | + // ~~~~~~~~~~~~~~~~~ | |
| 109 | + fmt.Println("\r\n~~~~~~~~~~ test fetching default path") | |
| 110 | + fetchAndTestDefault(t, url) | |
| 111 | + | |
| 112 | + // ~~~~~~~~~~~~~~~~~ | |
| 113 | + fmt.Println("\r\n~~~~~~~~~~ test fetching another page") | |
| 114 | + body0 = get(t, url+"/hols") | |
| 115 | + if !strings.Contains(body0, "glasgow.jpg") { | |
| 116 | + t.Fatal("fetching a subfolder failed") | |
| 117 | + } | |
| 118 | + | |
| 119 | + // ~~~~~~~~~~~~~~~~~ | |
| 120 | + fmt.Println("\r\n~~~~~~~~~~ test fetching an invalid path - redirected to root") | |
| 121 | + fetchAndTestDefault(t, url+"../../") | |
| 122 | + fetchAndTestDefault(t, url+"hols/../../") | |
| 123 | + | |
| 124 | + // ~~~~~~~~~~~~~~~~~ | |
| 125 | + fmt.Println("\r\n~~~~~~~~~~ test fetching regular files") | |
| 126 | + body0 = get(t, url+"subdir_with%20space/file_with%20space.html") | |
| 127 | + body1 = get(t, url+"fancy-path/a") | |
| 128 | + if body0 != `<b>spacious!!</b> ` || body1 != `fancy! ` { | |
| 129 | + t.Fatal("fetching a regular file errored") | |
| 130 | + } | |
| 131 | + | |
| 132 | + // ~~~~~~~~~~~~~~~~~ | |
| 133 | + fmt.Println("\r\n~~~~~~~~~~ test fetching a invalid file") | |
| 134 | + path = "../../../../../../../../../../etc/passwd" | |
| 135 | + if !testExtra && get(t, url+path) != `error` { | |
| 136 | + t.Fatal("fetching a invalid file didnt errored") | |
| 137 | + } else if testExtra { | |
| 138 | + fetchAndTestDefault(t, url+path) // extra path will just redirect to root dir | |
| 139 | + } | |
| 140 | + | |
| 141 | + // ~~~~~~~~~~~~~~~~~ | |
| 142 | + fmt.Println("\r\n~~~~~~~~~~ test zipping of folder 中文") | |
| 143 | + len, foundFile := getZip(t, "檔案.html", url+"zip?zipPath=%2F%E4%B8%AD%E6%96%87%2F&zipName=%E4%B8%AD%E6%96%87") | |
| 144 | + if len != 1 || !foundFile { | |
| 145 | + t.Fatal("invalid zip generated") | |
| 146 | + } | |
| 147 | + | |
| 148 | + // ~~~~~~~~~~~~~~~~~ | |
| 149 | + fmt.Println("\r\n~~~~~~~~~~ test zipping of folder with hidden file") | |
| 150 | + _, foundHidden := getZip(t, ".hidden-folder/some-file", url+"zip?zipPath=%2fhols%2f&zipName=hols") | |
| 151 | + if foundHidden && !testExtra { | |
| 152 | + t.Fatal("invalid zip generated - shouldnt contain hidden folder") | |
| 153 | + } else if !foundHidden && testExtra { | |
| 154 | + t.Fatal("invalid zip generated - should contain hidden folder") | |
| 155 | + } | |
| 156 | + | |
| 157 | + // ~~~~~~~~~~~~~~~~~ | |
| 158 | + fmt.Println("\r\n~~~~~~~~~~ test zip invalid path") | |
| 159 | + body0 = get(t, url+"zip?zipPath=%2Ftmp&zipName=subdir") | |
| 160 | + println(body0) | |
| 161 | + if body0 != `error` { | |
| 162 | + t.Fatal("zip passed for invalid path") | |
| 163 | + } | |
| 164 | + | |
| 165 | + // ~~~~~~~~~~~~~~~~~ | |
| 166 | + fmt.Println("\r\n~~~~~~~~~~ test mkdir rpc") | |
| 167 | + body0 = postJSON(t, url+"rpc", `{"call":"mkdirp","args":["/AAA"]}`) | |
| 168 | + if body0 != `ok` { | |
| 169 | + t.Fatal("mkdir rpc errored") | |
| 170 | + } | |
| 171 | + | |
| 172 | + body0 = fetchAndTestDefault(t, url) | |
| 173 | + if !strings.Contains(body0, `href="AAA">AAA/</a>`) { | |
| 174 | + t.Fatal("mkdir rpc folder not created") | |
| 175 | + } | |
| 176 | + | |
| 177 | + // ~~~~~~~~~~~~~~~~~ | |
| 178 | + fmt.Println("\r\n~~~~~~~~~~ test invalid mkdir rpc") | |
| 179 | + body0 = postJSON(t, url+"rpc", `{"call":"mkdirp","args":["../BBB"]}`) | |
| 180 | + if body0 != `error` { | |
| 181 | + t.Fatal("invalid mkdir rpc didnt errored #0") | |
| 182 | + } | |
| 183 | + | |
| 184 | + body0 = postJSON(t, url+"rpc", `{"call":"mkdirp","args":["/../BBB"]}`) | |
| 185 | + if body0 != `error` { | |
| 186 | + t.Fatal("invalid mkdir rpc didnt errored #1") | |
| 187 | + } | |
| 188 | + | |
| 189 | + // ~~~~~~~~~~~~~~~~~ | |
| 190 | + fmt.Println("\r\n~~~~~~~~~~ test post file") | |
| 191 | + path = "%2F%E1%84%92%E1%85%A1%20%E1%84%92%E1%85%A1" // "하 하" encoded | |
| 192 | + payload = "123 하" | |
| 193 | + body0 = postDummyFile(t, url, path, payload) | |
| 194 | + body1 = get(t, url+path) | |
| 195 | + body2 = fetchAndTestDefault(t, url) | |
| 196 | + if body0 != `ok` || body1 != payload || !strings.Contains(body2, `href="%E1%84%92%E1%85%A1%20%E1%84%92%E1%85%A1">하 하</a>`) { | |
| 197 | + t.Fatal("post file errored") | |
| 198 | + } | |
| 199 | + | |
| 200 | + // ~~~~~~~~~~~~~~~~~ | |
| 201 | + fmt.Println("\r\n~~~~~~~~~~ test post file incorrect path") | |
| 202 | + body0 = postDummyFile(t, url, "%2E%2E"+path, payload) | |
| 203 | + if !strings.Contains(body0, `err`) { | |
| 204 | + t.Fatal("post file incorrect path didnt errored") | |
| 205 | + } | |
| 206 | + | |
| 207 | + // ~~~~~~~~~~~~~~~~~ | |
| 208 | + fmt.Println("\r\n~~~~~~~~~~ test post file") | |
| 209 | + path = "2024-01-02-10:36:58.png" | |
| 210 | + payload = "123123123123123123123123" | |
| 211 | + body0 = postDummyFile(t, url, path, payload) | |
| 212 | + body1 = get(t, url+path) | |
| 213 | + body2 = fetchAndTestDefault(t, url) | |
| 214 | + if body0 != `ok` || body1 != payload || !strings.Contains(body2, `href="2024-01-02-10:36:58.png"`) { | |
| 215 | + t.Fatal("post file errored") | |
| 216 | + } | |
| 217 | + | |
| 218 | + // ~~~~~~~~~~~~~~~~~ | |
| 219 | + fmt.Println("\r\n~~~~~~~~~~ test mv rpc") | |
| 220 | + body0 = postJSON(t, url+"rpc", `{"call":"mv","args":["/AAA", "/hols/AAA"]}`) | |
| 221 | + body1 = fetchAndTestDefault(t, url) | |
| 222 | + if body0 != `ok` || strings.Contains(body1, `href="AAA">AAA/</a></td> </tr>`) { | |
| 223 | + t.Fatal("mv rpc errored") | |
| 224 | + } | |
| 225 | + | |
| 226 | + // ~~~~~~~~~~~~~~~~~ | |
| 227 | + fmt.Println("\r\n~~~~~~~~~~ test upload in new folder") | |
| 228 | + payload = "test" | |
| 229 | + body0 = postDummyFile(t, url, "%2Fhols%2FAAA%2Fabcdef", payload) | |
| 230 | + body1 = get(t, url+"hols/AAA/abcdef") | |
| 231 | + if body0 != `ok` || body1 != payload { | |
| 232 | + t.Fatal("upload in new folder errored") | |
| 233 | + } | |
| 234 | + | |
| 235 | + // ~~~~~~~~~~~~~~~~~ | |
| 236 | + fmt.Println("\r\n~~~~~~~~~~ test symlink, should succeed: ", testExtra) | |
| 237 | + body0 = get(t, url+"/support/") | |
| 238 | + hasListing := strings.Contains(body0, `readme.md`) | |
| 239 | + body1 = get(t, url+"/support/readme.md") | |
| 240 | + hasReadme := strings.Contains(body1, `the master branch is automatically built and pushed`) | |
| 241 | + body2 = get(t, url) | |
| 242 | + hasMainListing := strings.Contains(body2, `href="support">support/</a>`) | |
| 243 | + | |
| 244 | + if !testExtra && hasReadme { | |
| 245 | + t.Fatal("error symlink file reached where illegal") | |
| 246 | + } else if testExtra && !hasReadme { | |
| 247 | + t.Fatal("error symlink file unreachable") | |
| 248 | + } | |
| 249 | + if !testExtra && hasListing { | |
| 250 | + t.Fatal("error symlink folder reached where illegal") | |
| 251 | + } else if testExtra && !hasListing { | |
| 252 | + t.Fatal("error symlink folder unreachable") | |
| 253 | + } | |
| 254 | + if !testExtra && hasMainListing { | |
| 255 | + t.Fatal("error symlink folder where illegal") | |
| 256 | + } else if testExtra && !hasMainListing { | |
| 257 | + t.Fatal("error symlink folder unreachable") | |
| 258 | + } | |
| 259 | + | |
| 260 | + if testExtra { | |
| 261 | + fmt.Println("\r\n~~~~~~~~~~ test symlink mkdir & cleanup") | |
| 262 | + body0 = postJSON(t, url+"rpc", `{"call":"mkdirp","args":["/support/testfolder"]}`) | |
| 263 | + if body0 != `ok` { | |
| 264 | + t.Fatal("error symlink mkdir") | |
| 265 | + } | |
| 266 | + | |
| 267 | + body0 = postJSON(t, url+"rpc", `{"call":"rm","args":["/support/testfolder"]}`) | |
| 268 | + if body0 != `ok` { | |
| 269 | + t.Fatal("error symlink rm") | |
| 270 | + } | |
| 271 | + } | |
| 272 | + | |
| 273 | + fmt.Println("\r\n~~~~~~~~~~ test hidden file, should succeed: ", testExtra) | |
| 274 | + body0 = get(t, url+"/.testhidden") | |
| 275 | + hasHidden := strings.Contains(body0, `test`) | |
| 276 | + if !testExtra && hasHidden { | |
| 277 | + t.Fatal("error hidden file reached where illegal") | |
| 278 | + } else if testExtra && !hasHidden { | |
| 279 | + t.Fatal("error hidden file unreachable") | |
| 280 | + } | |
| 281 | + | |
| 282 | + // | |
| 283 | + fmt.Println("\r\n~~~~~~~~~~ test upload in new folder") | |
| 284 | + payload = "test" | |
| 285 | + body0 = postDummyFile(t, url, "%2Fhols%2FAAA%2Fabcdef", payload) | |
| 286 | + body1 = get(t, url+"hols/AAA/abcdef") | |
| 287 | + if body0 != `ok` || body1 != payload { | |
| 288 | + t.Fatal("upload in new folder errored") | |
| 289 | + } | |
| 290 | + | |
| 291 | + // ~~~~~~~~~~~~~~~~~ | |
| 292 | + fmt.Println("\r\n~~~~~~~~~~ test rm rpc & cleanup") | |
| 293 | + body0 = postJSON(t, url+"rpc", `{"call":"rm","args":["/hols/AAA"]}`) | |
| 294 | + if body0 != `ok` { | |
| 295 | + t.Fatal("cleanup errored #0") | |
| 296 | + } | |
| 297 | + | |
| 298 | + body0 = get(t, url+"hols/AAA") | |
| 299 | + if !strings.Contains(body0, `error`) { | |
| 300 | + t.Fatal("cleanup errored #1") | |
| 301 | + } | |
| 302 | + | |
| 303 | + body0 = postJSON(t, url+"rpc", `{"call":"rm","args":["/하 하"]}`) | |
| 304 | + if body0 != `ok` { | |
| 305 | + t.Fatal("cleanup errored #2") | |
| 306 | + } | |
| 307 | + | |
| 308 | + fmt.Printf("\r\n=========\r\n") | |
| 309 | +} | |
| 310 | + | |
| 311 | +func doTestReadonly(t *testing.T, url string) { | |
| 312 | + var payload, path, body0, body1 string | |
| 313 | + | |
| 314 | + // ~~~~~~~~~~~~~~~~~ | |
| 315 | + fmt.Println("\r\n~~~~~~~~~~ test fetching default path") | |
| 316 | + fetchAndTestDefault(t, url) | |
| 317 | + | |
| 318 | + // ~~~~~~~~~~~~~~~~~ | |
| 319 | + fmt.Println("\r\n~~~~~~~~~~ test fetching an invalid path - redirected to root") | |
| 320 | + fetchAndTestDefault(t, url+"../../") | |
| 321 | + fetchAndTestDefault(t, url+"hols/../../") | |
| 322 | + | |
| 323 | + // ~~~~~~~~~~~~~~~~~ | |
| 324 | + fmt.Println("\r\n~~~~~~~~~~ test fetching regular files") | |
| 325 | + body0 = get(t, url+"subdir_with%20space/file_with%20space.html") | |
| 326 | + body1 = get(t, url+"fancy-path/a") | |
| 327 | + if body0 != `<b>spacious!!</b> ` || body1 != `fancy! ` { | |
| 328 | + t.Fatal("fetching a regular file errored") | |
| 329 | + } | |
| 330 | + | |
| 331 | + // ~~~~~~~~~~~~~~~~~ | |
| 332 | + fmt.Println("\r\n~~~~~~~~~~ test fetching a invalid file") | |
| 333 | + path = "../../../../../../../../../../etc/passwd" | |
| 334 | + if get(t, url+path) != `error` { | |
| 335 | + t.Fatal("fetching a invalid file didnt errored") | |
| 336 | + } | |
| 337 | + | |
| 338 | + // ~~~~~~~~~~~~~~~~~ | |
| 339 | + fmt.Println("\r\n~~~~~~~~~~ test mkdir rpc") | |
| 340 | + body0 = postJSON(t, url+"rpc", `{"call":"mkdirp","args":["/AAA"]}`) | |
| 341 | + if body0 == `ok` { | |
| 342 | + t.Fatal("mkdir rpc passed - should not be allowed") | |
| 343 | + } | |
| 344 | + | |
| 345 | + // ~~~~~~~~~~~~~~~~~ | |
| 346 | + fmt.Println("\r\n~~~~~~~~~~ test post file") | |
| 347 | + path = "%2F%E1%84%92%E1%85%A1%20%E1%84%92%E1%85%A1" // "하 하" encoded | |
| 348 | + payload = "123 하" | |
| 349 | + body0 = postDummyFile(t, url, path, payload) | |
| 350 | + get(t, url+path) | |
| 351 | + if body0 == `ok` { | |
| 352 | + t.Fatal("post file passed - should not be allowed") | |
| 353 | + } | |
| 354 | + | |
| 355 | + // ~~~~~~~~~~~~~~~~~ | |
| 356 | + fmt.Println("\r\n~~~~~~~~~~ test mv rpc") | |
| 357 | + body0 = postJSON(t, url+"rpc", `{"call":"mv","args":["/AAA", "/hols/AAA"]}`) | |
| 358 | + fetchAndTestDefault(t, url) | |
| 359 | + if body0 == `ok` { | |
| 360 | + t.Fatal("mv rpc passed - should not be allowed") | |
| 361 | + } | |
| 362 | + | |
| 363 | + // ~~~~~~~~~~~~~~~~~ | |
| 364 | + fmt.Println("\r\n~~~~~~~~~~ test rm rpc & cleanup") | |
| 365 | + body0 = postJSON(t, url+"rpc", `{"call":"rm","args":["/hols/AAA"]}`) | |
| 366 | + if body0 == `ok` { | |
| 367 | + t.Fatal("cleanup passed - should not be allowed") | |
| 368 | + } | |
| 369 | + | |
| 370 | + fmt.Printf("\r\n=========\r\n") | |
| 371 | +} | |
| 372 | + | |
| 373 | +func TestNormal(t *testing.T) { | |
| 374 | + fmt.Println("========== testing normal path ============") | |
| 375 | + doTestRegular(t, "http://127.0.0.1:8001/", false) | |
| 376 | +} | |
| 377 | + | |
| 378 | +func TestExtra(t *testing.T) { | |
| 379 | + fmt.Println("========== testing extras options ============") | |
| 380 | + doTestRegular(t, "http://127.0.0.1:8001/fancy-path/", true) | |
| 381 | +} | |
| 382 | + | |
| 383 | +func TestRo(t *testing.T) { | |
| 384 | + fmt.Println("========== testing read only ============") | |
| 385 | + doTestReadonly(t, "http://127.0.0.1:8001/") | |
| 386 | +} | |
| 387 | + | |
| 388 | +func TestRunMain(t *testing.T) { | |
| 389 | + main() | |
| 390 | +} | |