Compare commits
No commits in common. "1.x" and "master" have entirely different histories.
888 changed files with 58351 additions and 40788 deletions
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
# Shell scripts require LF
|
||||||
|
*.sh text eol=lf
|
108
.github/workflows/build-containers.yml
vendored
Normal file
108
.github/workflows/build-containers.yml
vendored
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
name: Build Docker containers
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
jobs:
|
||||||
|
build-client:
|
||||||
|
name: Build and push client/ Docker container
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Determine metadata
|
||||||
|
run: |
|
||||||
|
CLOSEST_VER="$(git describe --tags --abbrev=0 $GITHUB_SHA)"
|
||||||
|
CLOSEST_MAJOR_VER="$(echo ${CLOSEST_VER} | cut -d'.' -f1)"
|
||||||
|
CLOSEST_MINOR_VER="$(echo ${CLOSEST_VER} | cut -d'.' -f2)"
|
||||||
|
SHORT_COMMIT=$(echo $GITHUB_SHA | cut -c1-8)
|
||||||
|
BUILD_INFO="v${CLOSEST_VER}-${SHORT_COMMIT}"
|
||||||
|
BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
|
||||||
|
|
||||||
|
echo "major_tag=${CLOSEST_MAJOR_VER}" >> $GITHUB_ENV
|
||||||
|
echo "minor_tag=${CLOSEST_MAJOR_VER}.${CLOSEST_MINOR_VER}" >> $GITHUB_ENV
|
||||||
|
echo "build_info=${BUILD_INFO}" >> $GITHUB_ENV
|
||||||
|
echo "build_date=${BUILD_DATE}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
echo "Build Info: ${BUILD_INFO}"
|
||||||
|
echo "Build Date: ${BUILD_DATE}"
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
|
||||||
|
- name: Build container
|
||||||
|
run: >
|
||||||
|
docker buildx build --push
|
||||||
|
--platform linux/amd64,linux/arm/v7,linux/arm64/v8
|
||||||
|
--build-arg BUILD_INFO=${{ env.build_info }}
|
||||||
|
--build-arg BUILD_DATE=${{ env.build_date }}
|
||||||
|
--build-arg SOURCE_COMMIT=$GITHUB_SHA
|
||||||
|
--build-arg DOCKER_REPO=szurubooru/client
|
||||||
|
-t "szurubooru/client:latest"
|
||||||
|
-t "szurubooru/client:${{ env.major_tag }}"
|
||||||
|
-t "szurubooru/client:${{ env.minor_tag }}"
|
||||||
|
./client
|
||||||
|
|
||||||
|
build-server:
|
||||||
|
name: Build and push server/ Docker container
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Determine metadata
|
||||||
|
run: |
|
||||||
|
CLOSEST_VER="$(git describe --tags --abbrev=0 $GITHUB_SHA)"
|
||||||
|
CLOSEST_MAJOR_VER="$(echo ${CLOSEST_VER} | cut -d'.' -f1)"
|
||||||
|
CLOSEST_MINOR_VER="$(echo ${CLOSEST_VER} | cut -d'.' -f2)"
|
||||||
|
SHORT_COMMIT=$(echo $GITHUB_SHA | cut -c1-8)
|
||||||
|
BUILD_INFO="v${CLOSEST_VER}-${SHORT_COMMIT}"
|
||||||
|
BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
|
||||||
|
|
||||||
|
echo "major_tag=${CLOSEST_MAJOR_VER}" >> $GITHUB_ENV
|
||||||
|
echo "minor_tag=${CLOSEST_MAJOR_VER}.${CLOSEST_MINOR_VER}" >> $GITHUB_ENV
|
||||||
|
echo "build_info=${BUILD_INFO}" >> $GITHUB_ENV
|
||||||
|
echo "build_date=${BUILD_DATE}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
echo "Build Info: ${BUILD_INFO}"
|
||||||
|
echo "Build Date: ${BUILD_DATE}"
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
|
||||||
|
- name: Build container
|
||||||
|
run: >
|
||||||
|
docker buildx build --push
|
||||||
|
--platform linux/amd64,linux/arm/v7,linux/arm64/v8
|
||||||
|
--build-arg BUILD_DATE=${{ env.build_date }}
|
||||||
|
--build-arg SOURCE_COMMIT=$GITHUB_SHA
|
||||||
|
--build-arg DOCKER_REPO=szurubooru/server
|
||||||
|
-t "szurubooru/server:latest"
|
||||||
|
-t "szurubooru/server:${{ env.major_tag }}"
|
||||||
|
-t "szurubooru/server:${{ env.minor_tag }}"
|
||||||
|
./server
|
28
.github/workflows/run-unit-tests.yml
vendored
Normal file
28
.github/workflows/run-unit-tests.yml
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
name: Run unit tests
|
||||||
|
on: [push, pull_request]
|
||||||
|
jobs:
|
||||||
|
test-server:
|
||||||
|
name: Run pytest for server/
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
|
||||||
|
- name: Build test container
|
||||||
|
run: >
|
||||||
|
docker buildx build --load
|
||||||
|
--platform linux/amd64 --target testing
|
||||||
|
-t test_container
|
||||||
|
./server
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: >
|
||||||
|
docker run --rm -t test_container
|
||||||
|
--color=no
|
||||||
|
--cov-report=term-missing:skip-covered
|
||||||
|
--cov=szurubooru
|
||||||
|
szurubooru/
|
21
.gitignore
vendored
21
.gitignore
vendored
|
@ -1,3 +1,18 @@
|
||||||
node_modules
|
# User-specific configuration
|
||||||
vendor
|
config.yaml
|
||||||
composer.lock
|
.env
|
||||||
|
|
||||||
|
# Client Development Artifacts
|
||||||
|
*/*_modules/
|
||||||
|
client/public
|
||||||
|
|
||||||
|
# Server Development Artifacts
|
||||||
|
.coverage
|
||||||
|
.cache
|
||||||
|
server/**/lib/
|
||||||
|
server/**/bin/
|
||||||
|
server/**/pyvenv.cfg
|
||||||
|
__pycache__/
|
||||||
|
|
||||||
|
data/
|
||||||
|
sql/
|
||||||
|
|
62
.pre-commit-config.yaml
Normal file
62
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
repos:
|
||||||
|
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v4.4.0
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: check-yaml
|
||||||
|
- id: mixed-line-ending
|
||||||
|
|
||||||
|
|
||||||
|
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
||||||
|
rev: v1.4.2
|
||||||
|
hooks:
|
||||||
|
- id: remove-tabs
|
||||||
|
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: '23.1.0'
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
files: 'server/'
|
||||||
|
types: [python]
|
||||||
|
language_version: python3.9
|
||||||
|
|
||||||
|
- repo: https://github.com/PyCQA/isort
|
||||||
|
rev: '5.12.0'
|
||||||
|
hooks:
|
||||||
|
- id: isort
|
||||||
|
files: 'server/'
|
||||||
|
types: [python]
|
||||||
|
exclude: server/szurubooru/migrations/env.py
|
||||||
|
additional_dependencies:
|
||||||
|
- toml
|
||||||
|
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||||
|
rev: v2.7.1
|
||||||
|
hooks:
|
||||||
|
- id: prettier
|
||||||
|
files: client/js/
|
||||||
|
exclude: client/js/.gitignore
|
||||||
|
args: ['--config', 'client/.prettierrc.yml']
|
||||||
|
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-eslint
|
||||||
|
rev: v8.33.0
|
||||||
|
hooks:
|
||||||
|
- id: eslint
|
||||||
|
files: client/js/
|
||||||
|
args: ['--fix']
|
||||||
|
additional_dependencies:
|
||||||
|
- eslint-config-prettier
|
||||||
|
|
||||||
|
- repo: https://github.com/PyCQA/flake8
|
||||||
|
rev: '6.0.0'
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
files: server/szurubooru/
|
||||||
|
additional_dependencies:
|
||||||
|
- flake8-print
|
||||||
|
args: ['--config=server/.flake8']
|
||||||
|
|
||||||
|
fail_fast: true
|
||||||
|
exclude: LICENSE.md
|
255
INSTALL.md
255
INSTALL.md
|
@ -1,255 +0,0 @@
|
||||||
Prerequisites
|
|
||||||
-------------
|
|
||||||
|
|
||||||
In order to run `szurubooru`, you need to have installed following software:
|
|
||||||
|
|
||||||
- `Apache` 2.4+
|
|
||||||
- `mod_rewrite`
|
|
||||||
- `mod_mime_magic` (recommended)
|
|
||||||
- `PHP` 5.6.0+
|
|
||||||
- `pdo_mysql`
|
|
||||||
- `imagick` or `gd`
|
|
||||||
- `MySQL` or `MariaDB`
|
|
||||||
- `composer` (`PHP` package manager)
|
|
||||||
- `npm` (`node.js` package manager)
|
|
||||||
|
|
||||||
Optional software:
|
|
||||||
|
|
||||||
- `dump-gnash`, `swfrender` or `ffmpeg` for Flash thumbnails
|
|
||||||
- `ffmpegthumbnailer` or `ffmpeg` for video thumbnails
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Cloning the repository
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Download the repository somewhere you will it run from, or better yet, clone it
|
|
||||||
with `git`:
|
|
||||||
|
|
||||||
cd /srv/www/
|
|
||||||
git clone https://github.com/rr-/szurubooru booru-test
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Fetching dependencies
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
To fetch dependencies that `szurubooru` needs in order to run, enter following
|
|
||||||
commands in the terminal:
|
|
||||||
|
|
||||||
composer update
|
|
||||||
npm update
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Running `grunt` tasks
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
`szurubooru` uses `grunt` to run tasks like database upgrades and tests. In
|
|
||||||
order to use `grunt` from the terminal, you can use:
|
|
||||||
|
|
||||||
node_modules/grunt-cli/bin/grunt [TASK]
|
|
||||||
|
|
||||||
But since it's inconvenient, you can install it globally by running as
|
|
||||||
administrator:
|
|
||||||
|
|
||||||
npm install -g grunt-cli
|
|
||||||
|
|
||||||
This will add `grunt` to your PATH, making things much more human-friendly.
|
|
||||||
|
|
||||||
grunt [TASK]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Enabling required modules in `PHP`
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
Enable required modules in `php.ini` (or other configuration file, depending on
|
|
||||||
your setup):
|
|
||||||
|
|
||||||
;Linux
|
|
||||||
extension=pdo_mysql.so
|
|
||||||
|
|
||||||
;Windows
|
|
||||||
extension=php_pdo_mysql.dll
|
|
||||||
|
|
||||||
In order to draw thumbnails, `szurubooru` needs either `Imagick` or `gd2`:
|
|
||||||
|
|
||||||
;Linux
|
|
||||||
extension=imagick.so
|
|
||||||
extension=gd.so
|
|
||||||
|
|
||||||
;Windows
|
|
||||||
extension=php_imagick.dll
|
|
||||||
extension=php_gd2.dll
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Creating virtual server in Apache
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
In order to make `szurubooru` visible in your browser, you need to create a
|
|
||||||
virtual server. This guide focuses on `Apache` web server. Note that although
|
|
||||||
it should be also possible to host `szurubooru` with `nginx`, you'd need to
|
|
||||||
manually translate the rules inside `public_html/.htaccess` into `nginx`
|
|
||||||
configuration.
|
|
||||||
|
|
||||||
Creating virtual server for `Apache` comes with no surprises, basically all you
|
|
||||||
need is the most basic configuration:
|
|
||||||
|
|
||||||
<VirtualHost *:80>
|
|
||||||
ServerName example.com
|
|
||||||
DocumentRoot /path/to/szurubooru/public_html/
|
|
||||||
</VirtualHost>
|
|
||||||
|
|
||||||
`ServerName` specifies the domain under which `szurubooru` will be hosted.
|
|
||||||
`DocumentRoot` should point to the `public_html/` directory.
|
|
||||||
|
|
||||||
Some environments / configurations require extra steps to make things work - in
|
|
||||||
case you experience any problems, please consult the troubleshooting section
|
|
||||||
later in this file.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Enabling required modules in Apache
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
Enable required modules in `httpd.conf` (or other configuration file, depending
|
|
||||||
on your setup):
|
|
||||||
|
|
||||||
LoadModule rewrite_module mod_rewrite.so ;Linux
|
|
||||||
LoadModule rewrite_module modules/mod_rewrite.so ;Windows
|
|
||||||
|
|
||||||
Enable `PHP` support:
|
|
||||||
|
|
||||||
LoadModule php5_module /usr/lib/apache2/modules/libphp5.so ;Linux
|
|
||||||
LoadModule php5_module /path/to/php/php5apache2_4.dll ;Windows
|
|
||||||
AddType application/x-httpd-php .php
|
|
||||||
PHPIniDir /path/to/php/
|
|
||||||
|
|
||||||
Enable MIME auto-detection (not required, but recommended - `szurubooru`
|
|
||||||
doesn't use file extensions, and reporting correct `Content-Type` to browser is
|
|
||||||
always a good thing):
|
|
||||||
|
|
||||||
;Linux
|
|
||||||
LoadModule mime_magic_module mod_mime_magic.so
|
|
||||||
<IfModule mod_mime_magic.c>
|
|
||||||
MIMEMagicFile /etc/apache2/magic
|
|
||||||
</IfModule>
|
|
||||||
|
|
||||||
;Windows
|
|
||||||
LoadModule mime_magic_module modules/mod_mime_magic.so
|
|
||||||
<IfModule mod_mime_magic.c>
|
|
||||||
MIMEMagicFile conf/magic
|
|
||||||
</IfModule>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Overwriting configuration
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Everything that can be configured is stored in `data/config.ini` file. In order
|
|
||||||
to make changes there, copy the file and name it `local.ini` and place it in
|
|
||||||
`data/` directory as well. Make sure you don't edit the `data/config.ini` file
|
|
||||||
itself, especially if you want to contribute.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Setting up the database
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Before running `szurubooru` for first time, you need to set up the database.
|
|
||||||
`szurubooru` uses MySQL, so let's fire `mysql` and type following:
|
|
||||||
|
|
||||||
create user 'maria' identified by 'arkadia';
|
|
||||||
create database booru_test;
|
|
||||||
grant all privileges on *.* to 'maria'@'%' with grant option;
|
|
||||||
|
|
||||||
Then you need to provide the above credentials in the configuration files as
|
|
||||||
described in the previous section. Example `local.ini` file:
|
|
||||||
|
|
||||||
[database]
|
|
||||||
dsn = mysql:dbname=booru_test
|
|
||||||
user = maria
|
|
||||||
password = arkadia
|
|
||||||
|
|
||||||
After that, upgrade the database using following command:
|
|
||||||
|
|
||||||
grunt upgrade
|
|
||||||
|
|
||||||
This should be also executed every time database schema changes.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Compiling assets
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Generally HTML templates, CSS stylesheets and JS scripts are scattered over
|
|
||||||
many files. This is desirable for development environment, but creates large
|
|
||||||
network overhead for production environment. In order to minify the files into
|
|
||||||
smallest possible packages, run following command:
|
|
||||||
|
|
||||||
grunt build
|
|
||||||
|
|
||||||
This should create `public_html/app.min.js`, `public_html/app.min.css` and
|
|
||||||
`public_html/app.min.html`. `.htaccess` is configured so that if these files
|
|
||||||
exist, it will load them instead of development environment. To delete these
|
|
||||||
conveniently, you can run:
|
|
||||||
|
|
||||||
grunt clean
|
|
||||||
|
|
||||||
If, for any reason, you do not wish to minify the resources, you should at
|
|
||||||
least copy the dependencies fetched before to the `public_html/` directory with
|
|
||||||
following:
|
|
||||||
|
|
||||||
grunt copy
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Creating administrator account
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
By now, you should be able to view `szurubooru` in the browser. Registering
|
|
||||||
administrator account is simple - the first user to create an account
|
|
||||||
automatically becomes administrator and doesn't need e-mail activation.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Troubleshooting
|
|
||||||
---------------
|
|
||||||
|
|
||||||
1. Problems with `Apache` virtual servers
|
|
||||||
|
|
||||||
After reloading `Apache` configuration, if you find yourself unable to
|
|
||||||
connect to the server, make sure that connections are open, for example,
|
|
||||||
like this:
|
|
||||||
|
|
||||||
<Directory /path/to/szurubooru/public_html/>
|
|
||||||
Require all granted
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
(Note that `Apache` versions prior to 2.4 used `Allow from all` directive.)
|
|
||||||
|
|
||||||
Additionally, in order to access the virtual host from your machine, make
|
|
||||||
sure the domain name `example.com` supplied in `<VirtualHost/>` section is
|
|
||||||
included in your `hosts` file (usually `/etc/hosts` on Linux and
|
|
||||||
`C:/windows/system32/drivers/etc/hosts` on Windows).
|
|
||||||
|
|
||||||
If the site doesn't work for you, make sure `Apache` can parse `.htaccess`
|
|
||||||
files. If it can't, you need to set `AllowOverride` option to `yes`, for
|
|
||||||
example by putting following snippet inside the `<VirtualHost/>` section:
|
|
||||||
|
|
||||||
<Directory /path/to/szurubooru/public_html/>
|
|
||||||
AllowOverride All
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
2. Problems with `PHP` modules or registration
|
|
||||||
|
|
||||||
Make sure your `php.ini` path is correct. Make sure all the modules are
|
|
||||||
actually loaded by inspecting results of `phpinfo()` call - create small
|
|
||||||
file containing:
|
|
||||||
|
|
||||||
<?php phpinfo(); ?>
|
|
||||||
|
|
||||||
Then, run it in your browser and inspect the output, looking for missing
|
|
||||||
modules that were supposed to be loaded.
|
|
21
LICENSE
21
LICENSE
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Marcin Kurczewski
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
676
LICENSE.md
Normal file
676
LICENSE.md
Normal file
|
@ -0,0 +1,676 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
==========================
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
==========================
|
||||||
|
|
||||||
|
> Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
# Preamble
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
# TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
## 0. Definitions.
|
||||||
|
|
||||||
|
_"This License"_ refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
_"Copyright"_ also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
_"The Program"_ refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as _"you"_. _"Licensees"_ and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To _"modify"_ a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a _"modified version"_ of the
|
||||||
|
earlier work or a work _"based on"_ the earlier work.
|
||||||
|
|
||||||
|
A _"covered work"_ means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To _"propagate"_ a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To _"convey"_ a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
## 1. Source Code.
|
||||||
|
|
||||||
|
The _"source code"_ for a work means the preferred form of the work
|
||||||
|
for making modifications to it. _"Object code"_ means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A _"Standard Interface"_ means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The _"System Libraries"_ of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The _"Corresponding Source"_ for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
## 2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
## 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
## 4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
## 5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
## 6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A _"User Product"_ is either (1) a _"consumer product"_, which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
_"Installation Information"_ for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
## 7. Additional Terms.
|
||||||
|
|
||||||
|
_"Additional permissions"_ are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
## 8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
## 9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
## 10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An _"entity transaction"_ is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
## 11. Patents.
|
||||||
|
|
||||||
|
A _"contributor"_ is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's _"essential patent claims"_ are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
## 12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
## 13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
## 14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
## 15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
## 16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
## 17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
# END OF TERMS AND CONDITIONS
|
||||||
|
--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type 'show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands _'show w'_ and _'show c'_ should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
86
README.md
86
README.md
|
@ -1,64 +1,50 @@
|
||||||
![szurubooru](https://raw.githubusercontent.com/rr-/szurubooru/master/public_html/img/shrine.png)
|
# szurubooru
|
||||||
|
|
||||||
szurubooru
|
Szurubooru is an image board engine inspired by services such as Danbooru,
|
||||||
==========
|
Gelbooru and Moebooru dedicated for small and medium communities. Its name [has
|
||||||
|
its roots in Polish language and has onomatopeic meaning of scraping or
|
||||||
|
scrubbing](https://sjp.pwn.pl/sjp/;2527372). It is pronounced as *shoorubooru*.
|
||||||
|
|
||||||
## What is it?
|
## Features
|
||||||
|
|
||||||
`szurubooru` is a Danbooru-style board, a gallery where users can upload,
|
- Post content: images (JPG, PNG, GIF, animated GIF), videos (MP4, WEBM), Flash animations
|
||||||
browse, tag and comment images, video clips and flash animations.
|
- Ability to retrieve web video content using [yt-dlp](https://github.com/yt-dlp/yt-dlp)
|
||||||
|
- Post comments
|
||||||
Its name have its roots in Polish language and has onomatopoeic meaning of
|
- Post notes / annotations, including arbitrary polygons
|
||||||
scraping or scrubbing. It is pronounced *"shoorubooru"* [ˌʃuruˈburu].
|
- Rich JSON REST API ([see documentation](doc/API.md))
|
||||||
|
- Token based authentication for clients
|
||||||
## Licensing
|
- Rich search system
|
||||||
|
- Rich privilege system
|
||||||
Please see the file named
|
- Autocomplete in search and while editing tags
|
||||||
[`LICENSE`](https://github.com/rr-/szurubooru/blob/master/LICENSE).
|
- Tag categories
|
||||||
|
- Tag suggestions
|
||||||
|
- Tag implications (adding a tag automatically adds another)
|
||||||
|
- Tag aliases
|
||||||
|
- Pools and pool categories
|
||||||
|
- Duplicate detection
|
||||||
|
- Post rating and favoriting; comment rating
|
||||||
|
- Polished UI
|
||||||
|
- Browser configurable endless paging
|
||||||
|
- Browser configurable backdrop grid for transparent images
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Please see the file named
|
It is recommended that you use Docker for deployment.
|
||||||
[`INSTALL.md`](https://github.com/rr-/szurubooru/blob/master/INSTALL.md).
|
[See installation instructions.](doc/INSTALL.md)
|
||||||
|
|
||||||
## Bugs and feature requests
|
More installation resources, as well as related projects can be found on the
|
||||||
|
[GitHub project Wiki](https://github.com/rr-/szurubooru/wiki)
|
||||||
|
|
||||||
All bugs and suggestions should be reported as issues on the [Github
|
## Screenshots
|
||||||
repository page](https://github.com/rr-/szurubooru/issues). When reporting,
|
|
||||||
please do following:
|
|
||||||
|
|
||||||
1. Search for existing issues for possible duplicates. If something is related
|
Post list:
|
||||||
to your problem, comment on that issue instead of opening a new one.
|
|
||||||
2. If you found an issue and the issue is closed, feel free to reopen it.
|
|
||||||
3. If you're reporting a bug, create an isolated and reproducible scenario.
|
|
||||||
4. If you're filing a feature request, provide examples - what might be
|
|
||||||
obvious to you, might not be so obvious to the developers.
|
|
||||||
|
|
||||||
## Contributing the code
|
![20160908_180032_fsk](https://cloud.githubusercontent.com/assets/1045476/18356730/3f1123d6-75ee-11e6-85dd-88a7615243a0.png)
|
||||||
|
|
||||||
Here are some guidelines on how to contribute:
|
Post view:
|
||||||
|
|
||||||
- Keep your changes compact.
|
![20160908_180429_lmp](https://cloud.githubusercontent.com/assets/1045476/18356731/3f1566ee-75ee-11e6-9594-e86ca7347b0f.png)
|
||||||
- Respect coding standards - be consistent with existing code base.
|
|
||||||
- Watch your whitespace - don't leave any characters at the end of the lines.
|
|
||||||
- Always run tests before pushing.
|
|
||||||
- Before starting, see
|
|
||||||
[`INSTALL.md`](https://github.com/rr-/szurubooru/blob/master/INSTALL.md).
|
|
||||||
- Use `grunt` to do automatic tasks like minifying Javascript files or running
|
|
||||||
tests. Run `grunt --help` to see full list of available tasks.
|
|
||||||
|
|
||||||
## API
|
## License
|
||||||
|
|
||||||
`szurubooru` from version 0.9+ uses REST API. Currently there is no formal
|
[GPLv3](LICENSE.md).
|
||||||
documentation; source code behind REST layer lies in `src/Controllers/`
|
|
||||||
directory. In order to use the API, bear in mind that you need to:
|
|
||||||
|
|
||||||
1. Have actual user account on the server to do most things (depending on
|
|
||||||
privileges).
|
|
||||||
2. Authenticate your requests:
|
|
||||||
1. Send user credentials to `/auth`. You'll receive authentication token in
|
|
||||||
return.
|
|
||||||
2. Send this token in X-Authorization-Token header on subsequent requests.
|
|
||||||
|
|
||||||
Developers reserve right to change API at any time with neither prior notice
|
|
||||||
nor keeping backwards compatibility.
|
|
||||||
|
|
1
client/.babelrc
Normal file
1
client/.babelrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{ "presets": ["env"] }
|
4
client/.dockerignore
Normal file
4
client/.dockerignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules/*
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
**/.gitignore
|
12
client/.eslintrc.yml
Normal file
12
client/.eslintrc.yml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
env:
|
||||||
|
browser: true
|
||||||
|
commonjs: true
|
||||||
|
es6: true
|
||||||
|
extends: 'prettier'
|
||||||
|
globals:
|
||||||
|
Atomics: readonly
|
||||||
|
SharedArrayBuffer: readonly
|
||||||
|
ignorePatterns:
|
||||||
|
- build.js
|
||||||
|
parserOptions:
|
||||||
|
ecmaVersion: 11
|
5
client/.jscsrc
Normal file
5
client/.jscsrc
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
preset: "google",
|
||||||
|
fileExtensions: [".js", "jscs"],
|
||||||
|
validateIndentation: 4,
|
||||||
|
}
|
4
client/.prettierrc.yml
Normal file
4
client/.prettierrc.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
parser: babel
|
||||||
|
printWidth: 79
|
||||||
|
tabWidth: 4
|
||||||
|
quoteProps: consistent
|
44
client/Dockerfile
Normal file
44
client/Dockerfile
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
FROM --platform=$BUILDPLATFORM node:lts as builder
|
||||||
|
WORKDIR /opt/app
|
||||||
|
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY . ./
|
||||||
|
|
||||||
|
ARG BUILD_INFO="docker-latest"
|
||||||
|
ARG CLIENT_BUILD_ARGS=""
|
||||||
|
RUN BASE_URL="__BASEURL__" node build.js --gzip ${CLIENT_BUILD_ARGS}
|
||||||
|
|
||||||
|
|
||||||
|
FROM --platform=$BUILDPLATFORM scratch as approot
|
||||||
|
|
||||||
|
COPY docker-start.sh /
|
||||||
|
|
||||||
|
WORKDIR /etc/nginx
|
||||||
|
COPY nginx.conf.docker ./nginx.conf
|
||||||
|
|
||||||
|
WORKDIR /var/www
|
||||||
|
COPY --from=builder /opt/app/public/ .
|
||||||
|
|
||||||
|
|
||||||
|
FROM nginx:alpine as release
|
||||||
|
|
||||||
|
RUN apk --no-cache add dumb-init
|
||||||
|
COPY --from=approot / /
|
||||||
|
|
||||||
|
CMD ["/docker-start.sh"]
|
||||||
|
VOLUME ["/data"]
|
||||||
|
|
||||||
|
ARG DOCKER_REPO
|
||||||
|
ARG BUILD_DATE
|
||||||
|
ARG SOURCE_COMMIT
|
||||||
|
LABEL \
|
||||||
|
maintainer="" \
|
||||||
|
org.opencontainers.image.title="${DOCKER_REPO}" \
|
||||||
|
org.opencontainers.image.url="https://github.com/rr-/szurubooru" \
|
||||||
|
org.opencontainers.image.documentation="https://github.com/rr-/szurubooru/blob/${SOURCE_COMMIT}/doc/INSTALL.md" \
|
||||||
|
org.opencontainers.image.created="${BUILD_DATE}" \
|
||||||
|
org.opencontainers.image.source="https://github.com/rr-/szurubooru" \
|
||||||
|
org.opencontainers.image.revision="${SOURCE_COMMIT}" \
|
||||||
|
org.opencontainers.image.licenses="GPL-3.0"
|
424
client/build.js
Executable file
424
client/build.js
Executable file
|
@ -0,0 +1,424 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
const webapp_icons = [
|
||||||
|
{ name: 'android-chrome-192x192.png', size: 192 },
|
||||||
|
{ name: 'android-chrome-512x512.png', size: 512 },
|
||||||
|
{ name: 'apple-touch-icon.png', size: 180 },
|
||||||
|
{ name: 'mstile-150x150.png', size: 150 }
|
||||||
|
];
|
||||||
|
|
||||||
|
const webapp_splash_screens = [
|
||||||
|
{ w: 640, h: 1136, center: 320 },
|
||||||
|
{ w: 750, h: 1294, center: 375 },
|
||||||
|
{ w: 1125, h: 2436, center: 565 },
|
||||||
|
{ w: 1242, h: 2148, center: 625 },
|
||||||
|
{ w: 1536, h: 2048, center: 770 },
|
||||||
|
{ w: 1668, h: 2224, center: 820 },
|
||||||
|
{ w: 2048, h: 2732, center: 1024 }
|
||||||
|
];
|
||||||
|
|
||||||
|
const external_js = [
|
||||||
|
'dompurify',
|
||||||
|
'js-cookie',
|
||||||
|
'marked',
|
||||||
|
'mousetrap',
|
||||||
|
'nprogress',
|
||||||
|
'superagent',
|
||||||
|
'underscore',
|
||||||
|
];
|
||||||
|
|
||||||
|
const app_manifest = {
|
||||||
|
name: 'szurubooru',
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: baseUrl() + 'img/android-chrome-192x192.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '192x192'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: baseUrl() + 'img/android-chrome-512x512.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '512x512'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
start_url: baseUrl(),
|
||||||
|
theme_color: '#24aadd',
|
||||||
|
background_color: '#ffffff',
|
||||||
|
display: 'standalone'
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const glob = require('glob');
|
||||||
|
const path = require('path');
|
||||||
|
const util = require('util');
|
||||||
|
const execSync = require('child_process').execSync;
|
||||||
|
const browserify = require('browserify');
|
||||||
|
const chokidar = require('chokidar');
|
||||||
|
const WebSocket = require('ws');
|
||||||
|
var PrettyError = require('pretty-error');
|
||||||
|
var pe = new PrettyError();
|
||||||
|
|
||||||
|
function readTextFile(path) {
|
||||||
|
return fs.readFileSync(path, 'utf-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
function gzipFile(file) {
|
||||||
|
file = path.normalize(file);
|
||||||
|
execSync('gzip -6 -k ' + file);
|
||||||
|
}
|
||||||
|
|
||||||
|
function baseUrl() {
|
||||||
|
return process.env.BASE_URL ? process.env.BASE_URL : '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
function bundleHtml() {
|
||||||
|
const underscore = require('underscore');
|
||||||
|
const babelify = require('babelify');
|
||||||
|
|
||||||
|
function minifyHtml(html) {
|
||||||
|
return require('html-minifier').minify(html, {
|
||||||
|
removeComments: true,
|
||||||
|
collapseWhitespace: true,
|
||||||
|
conservativeCollapse: true,
|
||||||
|
}).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseHtml = readTextFile('./html/index.htm')
|
||||||
|
.replace('<!-- Base HTML Placeholder -->', `<base href="${baseUrl()}"/>`);
|
||||||
|
fs.writeFileSync('./public/index.htm', minifyHtml(baseHtml));
|
||||||
|
|
||||||
|
let compiledTemplateJs = [
|
||||||
|
`'use strict';`,
|
||||||
|
`let _ = require('underscore');`,
|
||||||
|
`let templates = {};`
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const file of glob.sync('./html/**/*.tpl')) {
|
||||||
|
const name = path.basename(file, '.tpl').replace(/_/g, '-');
|
||||||
|
const placeholders = [];
|
||||||
|
let templateText = readTextFile(file);
|
||||||
|
templateText = templateText.replace(
|
||||||
|
/<%.*?%>/ig,
|
||||||
|
(match) => {
|
||||||
|
const ret = '%%%TEMPLATE' + placeholders.length;
|
||||||
|
placeholders.push(match);
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
templateText = minifyHtml(templateText);
|
||||||
|
templateText = templateText.replace(
|
||||||
|
/%%%TEMPLATE(\d+)/g,
|
||||||
|
(match, number) => { return placeholders[number]; });
|
||||||
|
|
||||||
|
const functionText = underscore.template(
|
||||||
|
templateText, { variable: 'ctx' }).source;
|
||||||
|
|
||||||
|
compiledTemplateJs.push(`templates['${name}'] = ${functionText};`);
|
||||||
|
}
|
||||||
|
compiledTemplateJs.push('module.exports = templates;');
|
||||||
|
|
||||||
|
fs.writeFileSync('./js/.templates.autogen.js', compiledTemplateJs.join('\n'));
|
||||||
|
console.info('Bundled HTML');
|
||||||
|
}
|
||||||
|
|
||||||
|
function bundleCss() {
|
||||||
|
const stylus = require('stylus');
|
||||||
|
|
||||||
|
function minifyCss(css) {
|
||||||
|
return require('csso').minify(css).css;
|
||||||
|
}
|
||||||
|
|
||||||
|
let css = '';
|
||||||
|
for (const file of glob.sync('./css/**/*.styl')) {
|
||||||
|
css += stylus.render(readTextFile(file), { filename: file });
|
||||||
|
}
|
||||||
|
fs.writeFileSync('./public/css/app.min.css', minifyCss(css));
|
||||||
|
if (process.argv.includes('--gzip')) {
|
||||||
|
gzipFile('./public/css/app.min.css');
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.copyFileSync(
|
||||||
|
'./node_modules/font-awesome/css/font-awesome.min.css',
|
||||||
|
'./public/css/vendor.min.css');
|
||||||
|
if (process.argv.includes('--gzip')) {
|
||||||
|
gzipFile('./public/css/vendor.min.css');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.info('Bundled CSS');
|
||||||
|
}
|
||||||
|
|
||||||
|
function minifyJs(path) {
|
||||||
|
return require('terser').minify(
|
||||||
|
fs.readFileSync(path, 'utf-8'), { compress: { unused: false } }).code;
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeJsBundle(b, path, compress, callback) {
|
||||||
|
let outputFile = fs.createWriteStream(path);
|
||||||
|
b.bundle().on('error', (e) => console.error(pe.render(e))).pipe(outputFile);
|
||||||
|
outputFile.on('finish', () => {
|
||||||
|
if (compress) {
|
||||||
|
fs.writeFileSync(path, minifyJs(path));
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bundleVendorJs(compress) {
|
||||||
|
let b = browserify();
|
||||||
|
for (let lib of external_js) {
|
||||||
|
b.require(lib);
|
||||||
|
}
|
||||||
|
if (!process.argv.includes('--no-transpile')) {
|
||||||
|
b.add(require.resolve('babel-polyfill'));
|
||||||
|
}
|
||||||
|
const file = './public/js/vendor.min.js';
|
||||||
|
writeJsBundle(b, file, compress, () => {
|
||||||
|
if (process.argv.includes('--gzip')) {
|
||||||
|
gzipFile(file);
|
||||||
|
}
|
||||||
|
console.info('Bundled vendor JS');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bundleAppJs(b, compress, callback) {
|
||||||
|
const file = './public/js/app.min.js';
|
||||||
|
writeJsBundle(b, file, compress, () => {
|
||||||
|
if (process.argv.includes('--gzip')) {
|
||||||
|
gzipFile(file);
|
||||||
|
}
|
||||||
|
console.info('Bundled app JS');
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function bundleJs() {
|
||||||
|
if (!process.argv.includes('--no-vendor-js')) {
|
||||||
|
bundleVendorJs(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.argv.includes('--no-app-js')) {
|
||||||
|
let watchify = require('watchify');
|
||||||
|
let b = browserify({ debug: process.argv.includes('--debug') });
|
||||||
|
if (!process.argv.includes('--no-transpile')) {
|
||||||
|
b = b.transform('babelify');
|
||||||
|
}
|
||||||
|
b = b.external(external_js).add(glob.sync('./js/**/*.js'));
|
||||||
|
const compress = !process.argv.includes('--debug');
|
||||||
|
bundleAppJs(b, compress, () => { });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const environment = process.argv.includes('--watch') ? "development" : "production";
|
||||||
|
|
||||||
|
function bundleConfig() {
|
||||||
|
function getVersion() {
|
||||||
|
let build_info = process.env.BUILD_INFO;
|
||||||
|
if (!build_info) {
|
||||||
|
try {
|
||||||
|
build_info = execSync('git describe --always --dirty --long --tags').toString();
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Cannot find build version');
|
||||||
|
build_info = 'unknown';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return build_info.trim();
|
||||||
|
}
|
||||||
|
const config = {
|
||||||
|
meta: {
|
||||||
|
version: getVersion(),
|
||||||
|
buildDate: new Date().toUTCString()
|
||||||
|
},
|
||||||
|
environment: environment
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.writeFileSync('./js/.config.autogen.json', JSON.stringify(config));
|
||||||
|
console.info('Generated config file');
|
||||||
|
}
|
||||||
|
|
||||||
|
function bundleBinaryAssets() {
|
||||||
|
fs.copyFileSync('./img/favicon.png', './public/img/favicon.png');
|
||||||
|
console.info('Copied images');
|
||||||
|
|
||||||
|
fs.copyFileSync('./fonts/open_sans.woff2', './public/fonts/open_sans.woff2')
|
||||||
|
for (let file of glob.sync('./node_modules/font-awesome/fonts/*.*')) {
|
||||||
|
if (fs.lstatSync(file).isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fs.copyFileSync(file, path.join('./public/fonts/', path.basename(file)));
|
||||||
|
}
|
||||||
|
if (process.argv.includes('--gzip')) {
|
||||||
|
for (let file of glob.sync('./public/fonts/*.*')) {
|
||||||
|
if (file.endsWith('woff2')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
gzipFile(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.info('Copied fonts')
|
||||||
|
}
|
||||||
|
|
||||||
|
function bundleWebAppFiles() {
|
||||||
|
const Jimp = require('jimp');
|
||||||
|
|
||||||
|
fs.writeFileSync('./public/manifest.json', JSON.stringify(app_manifest));
|
||||||
|
console.info('Generated app manifest');
|
||||||
|
|
||||||
|
Promise.all(webapp_icons.map(icon => {
|
||||||
|
return Jimp.read('./img/app.png')
|
||||||
|
.then(file => {
|
||||||
|
file.resize(icon.size, Jimp.AUTO, Jimp.RESIZE_BEZIER)
|
||||||
|
.write(path.join('./public/img/', icon.name));
|
||||||
|
});
|
||||||
|
}))
|
||||||
|
.then(() => {
|
||||||
|
console.info('Generated webapp icons');
|
||||||
|
});
|
||||||
|
|
||||||
|
Promise.all(webapp_splash_screens.map(dim => {
|
||||||
|
return Jimp.read('./img/splash.png')
|
||||||
|
.then(file => {
|
||||||
|
file.resize(dim.center, Jimp.AUTO, Jimp.RESIZE_BEZIER)
|
||||||
|
.background(0xFFFFFFFF)
|
||||||
|
.contain(dim.w, dim.center,
|
||||||
|
Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE)
|
||||||
|
.contain(dim.w, dim.h,
|
||||||
|
Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE)
|
||||||
|
.write(path.join('./public/img/',
|
||||||
|
'apple-touch-startup-image-' + dim.w + 'x' + dim.h + '.png'));
|
||||||
|
});
|
||||||
|
}))
|
||||||
|
.then(() => {
|
||||||
|
console.info('Generated splash screens');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeOutputDirs() {
|
||||||
|
const dirs = [
|
||||||
|
'./public',
|
||||||
|
'./public/css',
|
||||||
|
'./public/fonts',
|
||||||
|
'./public/img',
|
||||||
|
'./public/js'
|
||||||
|
];
|
||||||
|
for (let dir of dirs) {
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
fs.mkdirSync(dir, 0o755);
|
||||||
|
console.info('Created directory: ' + dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function watch() {
|
||||||
|
let wss = new WebSocket.Server({ port: 8080 });
|
||||||
|
const liveReload = !process.argv.includes('--no-live-reload');
|
||||||
|
|
||||||
|
function emitReload() {
|
||||||
|
if (liveReload) {
|
||||||
|
console.log("Requesting live reload.")
|
||||||
|
wss.clients.forEach((client) => {
|
||||||
|
if (client.readyState === WebSocket.OPEN) {
|
||||||
|
client.send("reload");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chokidar.watch('./fonts/**/*').on('change', () => {
|
||||||
|
try {
|
||||||
|
bundleBinaryAssets();
|
||||||
|
emitReload();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(pe.render(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chokidar.watch('./img/**/*').on('change', () => {
|
||||||
|
try {
|
||||||
|
bundleWebAppFiles();
|
||||||
|
emitReload();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(pe.render(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chokidar.watch('./html/**/*.tpl').on('change', () => {
|
||||||
|
try {
|
||||||
|
bundleHtml();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(pe.render(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chokidar.watch('./css/**/*.styl').on('change', () => {
|
||||||
|
try {
|
||||||
|
bundleCss()
|
||||||
|
emitReload();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(pe.render(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
bundleBinaryAssets();
|
||||||
|
bundleWebAppFiles();
|
||||||
|
bundleCss();
|
||||||
|
bundleHtml();
|
||||||
|
|
||||||
|
bundleVendorJs(true);
|
||||||
|
|
||||||
|
let watchify = require('watchify');
|
||||||
|
let b = browserify({
|
||||||
|
debug: process.argv.includes('--debug'),
|
||||||
|
entries: ['js/main.js'],
|
||||||
|
cache: {},
|
||||||
|
packageCache: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
b.plugin(watchify);
|
||||||
|
|
||||||
|
if (!process.argv.includes('--no-transpile')) {
|
||||||
|
b = b.transform('babelify');
|
||||||
|
}
|
||||||
|
b = b.external(external_js).add(glob.sync('./js/**/*.js'));
|
||||||
|
const compress = false;
|
||||||
|
|
||||||
|
function bundle(id) {
|
||||||
|
console.info("Rebundling app JS...");
|
||||||
|
let start = new Date();
|
||||||
|
bundleAppJs(b, compress, () => {
|
||||||
|
let end = new Date() - start;
|
||||||
|
console.info('Rebundled in %ds.', end / 1000)
|
||||||
|
emitReload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
b.on('update', bundle);
|
||||||
|
bundle();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
console.log("Building for '" + environment + "' environment.");
|
||||||
|
makeOutputDirs();
|
||||||
|
bundleConfig();
|
||||||
|
if (process.argv.includes('--watch')) {
|
||||||
|
watch();
|
||||||
|
} else {
|
||||||
|
if (!process.argv.includes('--no-binary-assets')) {
|
||||||
|
bundleBinaryAssets();
|
||||||
|
}
|
||||||
|
if (!process.argv.includes('--no-web-app-files')) {
|
||||||
|
bundleWebAppFiles();
|
||||||
|
}
|
||||||
|
if (!process.argv.includes('--no-html')) {
|
||||||
|
bundleHtml();
|
||||||
|
}
|
||||||
|
if (!process.argv.includes('--no-css')) {
|
||||||
|
bundleCss();
|
||||||
|
}
|
||||||
|
if (!process.argv.includes('--no-js')) {
|
||||||
|
bundleJs();
|
||||||
|
}
|
||||||
|
}
|
64
client/css/colors.styl
Normal file
64
client/css/colors.styl
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
$main-color = #24AADD
|
||||||
|
$window-color = white
|
||||||
|
$window-color-darktheme = #1a1a1a
|
||||||
|
$top-navigation-color = #F5F5F5
|
||||||
|
$top-navigation-color-darktheme = #333333
|
||||||
|
$text-color = #111
|
||||||
|
$text-color-darktheme = #e6e6e6
|
||||||
|
$inactive-link-color = #888
|
||||||
|
$inactive-link-color-darktheme = #cccccc
|
||||||
|
$line-color = #DDD
|
||||||
|
$active-tab-background-color = rgba(0, 0, 0, 0.06)
|
||||||
|
$focused-tab-background-color = rgba(0, 0, 0, 0.03)
|
||||||
|
$active-tab-background-color-darktheme = rgba(255, 255, 255, 0.06)
|
||||||
|
$focused-tab-background-color-darktheme = rgba(255, 255, 255, 0.03)
|
||||||
|
$message-info-border-color = #BDF
|
||||||
|
$message-info-background-color = #E3EFF9
|
||||||
|
$message-error-border-color = #FCC
|
||||||
|
$message-error-background-color = #FFF5F5
|
||||||
|
$message-success-border-color = #D3E3D3
|
||||||
|
$message-success-background-color = #F5FFF5
|
||||||
|
$input-bad-border-color = #FCC
|
||||||
|
$input-bad-background-color = #FFF5F5
|
||||||
|
$input-good-border-color = #D3E3D3
|
||||||
|
$input-good-background-color = #F5FFF5
|
||||||
|
$input-enabled-background-color = #FAFAFA
|
||||||
|
$input-enabled-border-color = #EEE
|
||||||
|
$input-enabled-text-color = $text-color
|
||||||
|
$input-enabled-text-color-darktheme = $text-color-darktheme
|
||||||
|
$input-disabled-background-color = #FAFAFA
|
||||||
|
$input-disabled-border-color = #EEE
|
||||||
|
$input-disabled-text-color = #888
|
||||||
|
$button-enabled-text-color = white
|
||||||
|
$button-enabled-background-color = $main-color
|
||||||
|
$button-disabled-text-color = #666
|
||||||
|
$button-disabled-background-color = #CCC
|
||||||
|
$post-thumbnail-border-color = $main-color
|
||||||
|
$post-thumbnail-no-tags-border-color = #F44
|
||||||
|
$default-tag-category-background-color = $active-tab-background-color
|
||||||
|
$new-tag-background-color = #DFC
|
||||||
|
$new-tag-text-color = black
|
||||||
|
$implied-tag-background-color = #FFC
|
||||||
|
$implied-tag-text-color = black
|
||||||
|
$tag-suggestions-header-color = #EEE
|
||||||
|
$tag-suggestions-border-color = #AAA
|
||||||
|
$duplicate-tag-background-color = #FDC
|
||||||
|
$duplicate-tag-text-color = black
|
||||||
|
$active-note-overlay-background-color = rgba(255, 255, 255, 0.3)
|
||||||
|
$active-note-overlay-border-color = rgba(62, 255, 62, 0.8)
|
||||||
|
$note-background-color = rgba(255, 255, 205, 0.3)
|
||||||
|
$note-border-color = rgba(0, 0, 0, 0.2)
|
||||||
|
$edited-note-background-color = rgba(222, 255, 222, 0.3)
|
||||||
|
$edited-note-border-color = rgba(0, 200, 0, 0.9)
|
||||||
|
$note-text-background-color = lemonchiffon
|
||||||
|
$note-text-border-color = black
|
||||||
|
$note-text-text-color = black
|
||||||
|
$first-note-point-color = orangered
|
||||||
|
$hovered-note-point-color = red
|
||||||
|
$hovered-first-note-point-color = red
|
||||||
|
$safety-safe = #88D488
|
||||||
|
$safety-sketchy = #F3D75F
|
||||||
|
$safety-unsafe = #F3985F
|
||||||
|
$scrollbar-thumb-color = $main-color
|
||||||
|
$scrollbar-bg-color = $input-enabled-background-color
|
||||||
|
$transparency-grid-square-color = #00000000
|
173
client/css/comment-control.styl
Normal file
173
client/css/comment-control.styl
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
@import colors
|
||||||
|
$comment-header-background-color = $top-navigation-color
|
||||||
|
$comment-header-background-color-darktheme = $top-navigation-color-darktheme
|
||||||
|
|
||||||
|
$comment-border-color = #DDD
|
||||||
|
|
||||||
|
.comment-container
|
||||||
|
padding: 0 0 0 60px
|
||||||
|
|
||||||
|
.avatar
|
||||||
|
float: left
|
||||||
|
margin-left: -60px
|
||||||
|
vertical-align: top
|
||||||
|
|
||||||
|
.thumbnail
|
||||||
|
width: 40px
|
||||||
|
height: 40px
|
||||||
|
a
|
||||||
|
display: inline-block
|
||||||
|
|
||||||
|
nav:not(.active), .tab:not(.active)
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.comment
|
||||||
|
border: 1px solid $comment-border-color
|
||||||
|
|
||||||
|
header
|
||||||
|
white-space: nowrap
|
||||||
|
font-size: 95%
|
||||||
|
vertical-align: middle
|
||||||
|
position: relative
|
||||||
|
background: $comment-header-background-color
|
||||||
|
border-bottom: 1px solid $comment-border-color
|
||||||
|
|
||||||
|
nav.edit
|
||||||
|
padding: 0.25em 1em 0 1em
|
||||||
|
line-height: 2em
|
||||||
|
ul
|
||||||
|
list-style-type: none
|
||||||
|
margin: -1px 0 -1px 0
|
||||||
|
padding: 0
|
||||||
|
li
|
||||||
|
display: inline-block
|
||||||
|
border: 1px solid transparent
|
||||||
|
a
|
||||||
|
padding: 0 1em
|
||||||
|
&.active
|
||||||
|
background: $window-color
|
||||||
|
border: 1px solid $comment-border-color
|
||||||
|
border-bottom: 1px solid $window-color
|
||||||
|
|
||||||
|
nav.readonly
|
||||||
|
padding: 0 1em
|
||||||
|
line-height: 2.25em
|
||||||
|
|
||||||
|
.date, .score-container, .edit
|
||||||
|
margin-right: 2em
|
||||||
|
|
||||||
|
.score-container, .link-container
|
||||||
|
display: inline-block
|
||||||
|
|
||||||
|
&:before
|
||||||
|
position: absolute
|
||||||
|
display: block
|
||||||
|
content: ' '
|
||||||
|
width: 0
|
||||||
|
height: 0
|
||||||
|
left: -1.5em
|
||||||
|
top: calc(50% - 0.75em)
|
||||||
|
border: 0.75em solid transparent
|
||||||
|
border-right: 0.75em solid darken($comment-border-color, 10%)
|
||||||
|
|
||||||
|
&:after
|
||||||
|
position: absolute
|
||||||
|
display: block
|
||||||
|
content: ' '
|
||||||
|
width: 0
|
||||||
|
height: 0
|
||||||
|
left: calc(-1.5em + 1px)
|
||||||
|
top: calc(50% - 0.75em)
|
||||||
|
border: 0.75em solid transparent
|
||||||
|
border-right: 0.75em solid $comment-header-background-color
|
||||||
|
|
||||||
|
.edit, .delete, .score-container a, .nickname a
|
||||||
|
&:not(.inactive)
|
||||||
|
color: mix($main-color, $inactive-link-color)
|
||||||
|
|
||||||
|
i
|
||||||
|
margin-right: 0.3em
|
||||||
|
.downvote i
|
||||||
|
text-align: right
|
||||||
|
.upvote i
|
||||||
|
display: inline-block
|
||||||
|
width: 1em
|
||||||
|
margin: 0
|
||||||
|
.value
|
||||||
|
text-align: center
|
||||||
|
display: inline-block
|
||||||
|
width: 2em
|
||||||
|
|
||||||
|
.body
|
||||||
|
width: auto
|
||||||
|
margin: 1em
|
||||||
|
|
||||||
|
.keep-height
|
||||||
|
position: relative
|
||||||
|
textarea
|
||||||
|
position: absolute
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
.tab.edit
|
||||||
|
min-height: 150px
|
||||||
|
|
||||||
|
.messages
|
||||||
|
margin: 1em 0
|
||||||
|
|
||||||
|
.darktheme .comment-container .comment header
|
||||||
|
background: $comment-header-background-color-darktheme
|
||||||
|
nav.edit
|
||||||
|
ul
|
||||||
|
li
|
||||||
|
&.active
|
||||||
|
background: $window-color-darktheme
|
||||||
|
border-bottom: 1px solid $window-color-darktheme
|
||||||
|
.edit, .delete, .score-container a, .nickname a
|
||||||
|
&:not(.inactive)
|
||||||
|
color: mix($main-color, $inactive-link-color-darktheme)
|
||||||
|
|
||||||
|
.comment-content
|
||||||
|
p
|
||||||
|
word-wrap: normal
|
||||||
|
word-break: break-word
|
||||||
|
|
||||||
|
ul, ol
|
||||||
|
list-style-position: inside
|
||||||
|
margin: 1em 0
|
||||||
|
padding: 0 0 0 1.5em
|
||||||
|
|
||||||
|
.sjis
|
||||||
|
font-family: 'MS PGothic', 'MS Pゴシック', 'IPAMonaPGothic', 'Trebuchet MS', Verdana, Futura, Arial, Helvetica, sans-serif
|
||||||
|
background: #fbfbfb
|
||||||
|
color: #111
|
||||||
|
font-size: 1em
|
||||||
|
line-height: 1
|
||||||
|
margin: 0
|
||||||
|
padding: 4px
|
||||||
|
overflow: auto
|
||||||
|
white-space: pre
|
||||||
|
word-wrap: normal
|
||||||
|
|
||||||
|
.spoiler
|
||||||
|
background: #eee
|
||||||
|
color: #eee
|
||||||
|
&:hover
|
||||||
|
color: dimgray
|
||||||
|
&:before
|
||||||
|
content: '['
|
||||||
|
color: #000
|
||||||
|
&:after
|
||||||
|
content: ']'
|
||||||
|
color: #000
|
||||||
|
|
||||||
|
blockquote
|
||||||
|
border-left: 3px solid #eee
|
||||||
|
margin-left: 0
|
||||||
|
padding: 0.3em 0.3em 0.3em 0.7em
|
||||||
|
background: #fafafa
|
||||||
|
color: #444
|
||||||
|
|
||||||
|
:first-child
|
||||||
|
margin-top: 0
|
||||||
|
:last-child
|
||||||
|
margin-bottom: 0
|
9
client/css/comment-list-control.styl
Normal file
9
client/css/comment-list-control.styl
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
.comments>ul
|
||||||
|
list-style-type: none
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
>li
|
||||||
|
margin-bottom: 1em
|
||||||
|
&:last-child
|
||||||
|
margin-bottom: 0
|
54
client/css/comment-list-view.styl
Normal file
54
client/css/comment-list-view.styl
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
@import colors
|
||||||
|
$comment-border-color = $top-navigation-color
|
||||||
|
$comment-border-color-darktheme = $top-navigation-color-darktheme
|
||||||
|
|
||||||
|
.global-comment-list
|
||||||
|
text-align: left
|
||||||
|
|
||||||
|
&>ul
|
||||||
|
list-style-type: none
|
||||||
|
margin: 1em 0 0
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
&>li
|
||||||
|
margin-top: 2em
|
||||||
|
padding-top: 2em
|
||||||
|
border-top: 3px solid $comment-border-color
|
||||||
|
&:first-child
|
||||||
|
margin-top: 0
|
||||||
|
padding-top: 0
|
||||||
|
border-top: none
|
||||||
|
|
||||||
|
@media (max-width: 700px)
|
||||||
|
.post-thumbnail
|
||||||
|
margin-bottom: 1em
|
||||||
|
.thumbnail
|
||||||
|
width: 50vw
|
||||||
|
height: 33vw
|
||||||
|
|
||||||
|
@media (min-width: 700px)
|
||||||
|
&>li
|
||||||
|
padding-left: 13em
|
||||||
|
.post-thumbnail
|
||||||
|
float: left
|
||||||
|
margin: 0 0 1em -13em
|
||||||
|
.thumbnail
|
||||||
|
width: 12em
|
||||||
|
height: 8em
|
||||||
|
|
||||||
|
&>li
|
||||||
|
clear: both
|
||||||
|
|
||||||
|
.post-thumbnail
|
||||||
|
vertical-align: top
|
||||||
|
margin-right: 1em
|
||||||
|
a
|
||||||
|
display: inline-block
|
||||||
|
|
||||||
|
.comments-container
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.darktheme .global-comment-list
|
||||||
|
&>ul
|
||||||
|
&>li
|
||||||
|
border-top: 3px solid $comment-border-color-darktheme
|
386
client/css/core-forms.styl
Normal file
386
client/css/core-forms.styl
Normal file
|
@ -0,0 +1,386 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
form
|
||||||
|
display: block
|
||||||
|
width: 20em
|
||||||
|
|
||||||
|
.input
|
||||||
|
list-style-type: none
|
||||||
|
margin: 0 0 2em 0
|
||||||
|
padding: 0
|
||||||
|
li
|
||||||
|
margin-top: 1.2em
|
||||||
|
label
|
||||||
|
display: block
|
||||||
|
padding: 0.3em 0
|
||||||
|
.input li:first-child label:not(.radio):not(.checkbox):not(.file-dropper),
|
||||||
|
.input li:first-child
|
||||||
|
padding-top: 0
|
||||||
|
margin-top: 0
|
||||||
|
|
||||||
|
form:not(.horizontal)
|
||||||
|
.hint
|
||||||
|
margin-top: 0.2em
|
||||||
|
margin-bottom: 0
|
||||||
|
color: $inactive-link-color
|
||||||
|
font-size: 80%
|
||||||
|
line-height: 120%
|
||||||
|
|
||||||
|
.darktheme form:not(.horizontal)
|
||||||
|
.hint
|
||||||
|
color: $inactive-link-color-darktheme
|
||||||
|
|
||||||
|
form.horizontal
|
||||||
|
display: inline-block
|
||||||
|
margin-bottom: 1em
|
||||||
|
.input, .buttons, ul
|
||||||
|
display: inline-block
|
||||||
|
vertical-align: top
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
input
|
||||||
|
vertical-align: top
|
||||||
|
.buttons
|
||||||
|
margin-right: 0.5em
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
display: block
|
||||||
|
.input, .buttons, ul
|
||||||
|
display: block
|
||||||
|
margin-top: 0.5em
|
||||||
|
&:first-child
|
||||||
|
margin-top: 0
|
||||||
|
.buttons
|
||||||
|
margin-right: 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Radio buttons and checkboxes
|
||||||
|
*/
|
||||||
|
input[type=radio], input[type=checkbox]
|
||||||
|
position: absolute
|
||||||
|
opacity: 0
|
||||||
|
|
||||||
|
.radio, .checkbox
|
||||||
|
box-sizing: border-box
|
||||||
|
position: relative
|
||||||
|
display: inline-block
|
||||||
|
padding-left: calc(20px + 0.5em) !important
|
||||||
|
vertical-align: middle
|
||||||
|
cursor: pointer
|
||||||
|
|
||||||
|
&:hover:before
|
||||||
|
border-color: $main-color
|
||||||
|
|
||||||
|
.radio:before
|
||||||
|
border-radius: 100%
|
||||||
|
|
||||||
|
.radio:before, .checkbox:before
|
||||||
|
transition: border-color 0.1s linear
|
||||||
|
position: absolute
|
||||||
|
left: 0
|
||||||
|
top: 0.15em
|
||||||
|
display: block
|
||||||
|
width: 16px
|
||||||
|
height: 16px
|
||||||
|
background: $input-enabled-background-color
|
||||||
|
border: 2px solid $input-enabled-border-color
|
||||||
|
content: ''
|
||||||
|
|
||||||
|
.radio:after
|
||||||
|
background: $main-color
|
||||||
|
transition: opacity 0.1s linear
|
||||||
|
position: absolute
|
||||||
|
left: 5px
|
||||||
|
top: 0.15em
|
||||||
|
margin-top: 5px
|
||||||
|
display: block
|
||||||
|
width: 10px
|
||||||
|
height: 10px
|
||||||
|
border-radius: 50%
|
||||||
|
content: ''
|
||||||
|
opacity: 0
|
||||||
|
|
||||||
|
.checkbox:after
|
||||||
|
transition: opacity 0.1s linear
|
||||||
|
position: absolute
|
||||||
|
top: 0.15em
|
||||||
|
left: 6px
|
||||||
|
display: block
|
||||||
|
margin-top: 3px
|
||||||
|
width: 5px
|
||||||
|
height: 9px
|
||||||
|
border-right: 3px solid $main-color
|
||||||
|
border-bottom: 3px solid $main-color
|
||||||
|
content: ''
|
||||||
|
opacity: 0
|
||||||
|
transform: rotate(45deg)
|
||||||
|
|
||||||
|
|
||||||
|
input[type=radio]:checked + .radio:before,
|
||||||
|
input[type=checkbox]:checked + .checkbox:before
|
||||||
|
border-color: $main-color
|
||||||
|
|
||||||
|
input[type=radio]:checked + .radio:after,
|
||||||
|
input[type=checkbox]:checked + .checkbox:after
|
||||||
|
opacity: 1
|
||||||
|
|
||||||
|
input[type=radio]:disabled + .radio:before,
|
||||||
|
input[type=checkbox]:disabled + .checkbox:before,
|
||||||
|
input[type=radio]:disabled + .radio:after,
|
||||||
|
input[type=checkbox]:disabled + .checkbox:after
|
||||||
|
border-color: $input-disabled-text-color
|
||||||
|
|
||||||
|
input[type=radio]:disabled + .radio,
|
||||||
|
input[type=checkbox]:disabled + .checkbox
|
||||||
|
border-color: $input-disabled-text-color
|
||||||
|
|
||||||
|
input[type=radio]:focus + .radio:before,
|
||||||
|
input[type=checkbox]:focus + .checkbox:before
|
||||||
|
border-color: $main-color
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Date and time inputs
|
||||||
|
*/
|
||||||
|
|
||||||
|
input[type=date],
|
||||||
|
input[type=time]
|
||||||
|
vertical-align: top
|
||||||
|
font-family: 'Droid Sans', sans-serif
|
||||||
|
font-size: 100%
|
||||||
|
padding: 0.2em 0.3em
|
||||||
|
box-sizing: border-box
|
||||||
|
border: 2px solid $input-enabled-border-color
|
||||||
|
background: $input-enabled-background-color
|
||||||
|
color: $input-enabled-text-color
|
||||||
|
box-shadow: none /* :-moz-submit-invalid on FF */
|
||||||
|
transition: border-color 0.1s linear, background-color 0.1s linear
|
||||||
|
|
||||||
|
&:disabled
|
||||||
|
border: 2px solid $input-disabled-border-color
|
||||||
|
background: $input-disabled-background-color
|
||||||
|
color: $input-disabled-text-color
|
||||||
|
|
||||||
|
&:focus
|
||||||
|
border-color: $main-color
|
||||||
|
|
||||||
|
&[readonly]
|
||||||
|
border: 2px solid $input-disabled-border-color
|
||||||
|
background: $input-disabled-background-color
|
||||||
|
color: $input-disabled-text-color
|
||||||
|
|
||||||
|
.darktheme
|
||||||
|
input[type=date],
|
||||||
|
input[type=time]
|
||||||
|
border: 2px solid darken($input-enabled-border-color, 75%)
|
||||||
|
background: darken($input-enabled-background-color, 75%)
|
||||||
|
color: $input-enabled-text-color-darktheme
|
||||||
|
&:disabled
|
||||||
|
background: darken($input-disabled-background-color, 75%)
|
||||||
|
&[readonly]
|
||||||
|
background: darken($input-disabled-background-color, 75%)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Regular inputs
|
||||||
|
*/
|
||||||
|
select,
|
||||||
|
textarea,
|
||||||
|
input[type=text],
|
||||||
|
input[type=email],
|
||||||
|
input[type=password],
|
||||||
|
input[type=number]
|
||||||
|
vertical-align: top
|
||||||
|
font-family: 'Droid Sans', sans-serif
|
||||||
|
font-size: 100%
|
||||||
|
padding: 0.2em 0.3em
|
||||||
|
text-overflow: ellipsis
|
||||||
|
width: 100%
|
||||||
|
box-sizing: border-box
|
||||||
|
border: 2px solid $input-enabled-border-color
|
||||||
|
background: $input-enabled-background-color
|
||||||
|
color: $input-enabled-text-color
|
||||||
|
box-shadow: none /* :-moz-submit-invalid on FF */
|
||||||
|
transition: border-color 0.1s linear, background-color 0.1s linear
|
||||||
|
|
||||||
|
&:disabled
|
||||||
|
border: 2px solid $input-disabled-border-color
|
||||||
|
background: $input-disabled-background-color
|
||||||
|
color: $input-disabled-text-color
|
||||||
|
|
||||||
|
&:focus
|
||||||
|
border-color: $main-color
|
||||||
|
|
||||||
|
&[readonly]
|
||||||
|
border: 2px solid $input-disabled-border-color
|
||||||
|
background: $input-disabled-background-color
|
||||||
|
color: $input-disabled-text-color
|
||||||
|
|
||||||
|
.darktheme
|
||||||
|
select,
|
||||||
|
textarea,
|
||||||
|
input[type=text],
|
||||||
|
input[type=email],
|
||||||
|
input[type=password],
|
||||||
|
input[type=number]
|
||||||
|
border: 2px solid darken($input-enabled-border-color, 75%)
|
||||||
|
background: darken($input-enabled-background-color, 75%)
|
||||||
|
color: $input-enabled-text-color-darktheme
|
||||||
|
&:disabled
|
||||||
|
background: darken($input-disabled-background-color, 75%)
|
||||||
|
&[readonly]
|
||||||
|
background: darken($input-disabled-background-color, 75%)
|
||||||
|
|
||||||
|
input[readonly],
|
||||||
|
input[readonly]+.radio,
|
||||||
|
input[readonly]+.checkbox,
|
||||||
|
input:disabled+.radio,
|
||||||
|
input:disabled+.checkbox,
|
||||||
|
input:disabled
|
||||||
|
cursor: not-allowed
|
||||||
|
|
||||||
|
label.color
|
||||||
|
white-space: nowrap
|
||||||
|
position: relative
|
||||||
|
display: flex
|
||||||
|
input[type=text]
|
||||||
|
margin-right: 0.25em
|
||||||
|
width: auto
|
||||||
|
.preview
|
||||||
|
display: inline-block
|
||||||
|
text-align: center
|
||||||
|
padding: 0 0.5em
|
||||||
|
border: 2px solid black
|
||||||
|
&:after
|
||||||
|
content: 'A'
|
||||||
|
.background-preview
|
||||||
|
border-right: 0
|
||||||
|
color: transparent
|
||||||
|
.text-preview
|
||||||
|
border-left: 0
|
||||||
|
|
||||||
|
|
||||||
|
form.show-validation .input
|
||||||
|
input:invalid
|
||||||
|
outline: 0
|
||||||
|
border: 2px solid $input-bad-border-color
|
||||||
|
background: $input-bad-background-color
|
||||||
|
input:valid
|
||||||
|
outline: 0
|
||||||
|
border: 2px solid $input-good-border-color
|
||||||
|
background: $input-good-background-color
|
||||||
|
.darktheme form.show-validation .input
|
||||||
|
input:valid
|
||||||
|
background: darken($input-good-background-color, 75%)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Buttons
|
||||||
|
*/
|
||||||
|
button,
|
||||||
|
input[type=button],
|
||||||
|
input[type=submit]
|
||||||
|
cursor: pointer
|
||||||
|
font-size: 100%
|
||||||
|
padding: 0.2em 0.7em
|
||||||
|
border-radius: 0
|
||||||
|
border: 2px solid $button-enabled-background-color
|
||||||
|
background: $button-enabled-background-color
|
||||||
|
color: $button-enabled-text-color
|
||||||
|
outline: 0 /* something on Chrome */
|
||||||
|
-moz-appearance: none
|
||||||
|
-webkit-appearance: none
|
||||||
|
|
||||||
|
&:disabled
|
||||||
|
cursor: default
|
||||||
|
border-color: $button-disabled-background-color
|
||||||
|
background-color: $button-disabled-background-color
|
||||||
|
color: $button-disabled-text-color
|
||||||
|
|
||||||
|
&.discourage
|
||||||
|
border-color: transparent
|
||||||
|
background-color: transparent
|
||||||
|
color: $button-disabled-text-color
|
||||||
|
|
||||||
|
&:focus
|
||||||
|
border: 2px solid $text-color
|
||||||
|
|
||||||
|
select:-moz-focusring
|
||||||
|
text-shadow: 0
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
input::-moz-focus-inner
|
||||||
|
border: 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File dropper
|
||||||
|
*/
|
||||||
|
.file-dropper-holder
|
||||||
|
.file-dropper
|
||||||
|
display: block
|
||||||
|
background: $window-color
|
||||||
|
border: 3px dashed #eee
|
||||||
|
padding: 0.3em 0.5em
|
||||||
|
line-height: 140%
|
||||||
|
text-align: center
|
||||||
|
cursor: pointer
|
||||||
|
overflow: hidden
|
||||||
|
word-wrap: break-word
|
||||||
|
.url-holder
|
||||||
|
display: flex
|
||||||
|
margin-top: 0.5em
|
||||||
|
input, button
|
||||||
|
min-width: 0 /* firefox being sassy */
|
||||||
|
width: auto !important /* don't inherit anything weird */
|
||||||
|
input
|
||||||
|
flex: 1
|
||||||
|
button
|
||||||
|
margin-left: 0.5em
|
||||||
|
|
||||||
|
.darktheme .file-dropper-holder
|
||||||
|
.file-dropper
|
||||||
|
background: $window-color-darktheme
|
||||||
|
|
||||||
|
input[type=file]:disabled+.file-dropper
|
||||||
|
cursor: default
|
||||||
|
opacity: .5
|
||||||
|
|
||||||
|
input[type=file]:active+.file-dropper,
|
||||||
|
input[type=file]:focus+.file-dropper,
|
||||||
|
.file-dropper.active
|
||||||
|
border-color: $main-color
|
||||||
|
|
||||||
|
.autocomplete
|
||||||
|
position: absolute
|
||||||
|
z-index: 10
|
||||||
|
background: $window-color
|
||||||
|
border: 2px solid $main-color
|
||||||
|
display: none
|
||||||
|
font-size: 0.95em
|
||||||
|
ul
|
||||||
|
list-style-type: none
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
li
|
||||||
|
margin: 0
|
||||||
|
a
|
||||||
|
display: block
|
||||||
|
padding: 0.1em 0.5em
|
||||||
|
&.active a, a:hover
|
||||||
|
background: $button-enabled-background-color
|
||||||
|
color: $button-enabled-text-color
|
||||||
|
span
|
||||||
|
color: $button-enabled-text-color
|
||||||
|
.disabled
|
||||||
|
color: $inactive-link-color
|
||||||
|
|
||||||
|
.darktheme .autocomplete
|
||||||
|
background: $window-color-darktheme
|
||||||
|
ul li .disabled
|
||||||
|
color: $inactive-link-color-darktheme
|
||||||
|
|
||||||
|
.anticomplete
|
||||||
|
display: none
|
327
client/css/core-general.styl
Normal file
327
client/css/core-general.styl
Normal file
|
@ -0,0 +1,327 @@
|
||||||
|
@import colors
|
||||||
|
@import mixins
|
||||||
|
|
||||||
|
$active-tab-text-color = $text-color
|
||||||
|
$active-tab-text-color-darktheme = $text-color-darktheme
|
||||||
|
$inactive-tab-text-color = $inactive-link-color
|
||||||
|
$inactive-tab-text-color-darktheme = $inactive-link-color-darktheme
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local('Open Sans'), local('OpenSans'), url(../fonts/open_sans.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||||
|
|
||||||
|
/* make <body> cover entire viewport */
|
||||||
|
html
|
||||||
|
height: 100%
|
||||||
|
body
|
||||||
|
min-height: 100%
|
||||||
|
|
||||||
|
body
|
||||||
|
background: $window-color
|
||||||
|
overflow-y: scroll
|
||||||
|
margin: 0
|
||||||
|
color: $text-color
|
||||||
|
font-family: 'Open Sans', sans-serif
|
||||||
|
font-size: 1em
|
||||||
|
line-height: 1.4
|
||||||
|
@media (max-width: 800px)
|
||||||
|
font-size: 0.875em
|
||||||
|
@media (max-width: 1200px)
|
||||||
|
font-size: 0.95em
|
||||||
|
|
||||||
|
body.darktheme
|
||||||
|
color: $text-color-darktheme
|
||||||
|
background: $window-color-darktheme
|
||||||
|
|
||||||
|
h1, h2, h3
|
||||||
|
font-weight: normal
|
||||||
|
margin-bottom: 1em
|
||||||
|
|
||||||
|
h1
|
||||||
|
font-size: 2em
|
||||||
|
|
||||||
|
h2
|
||||||
|
font-size: 1.5em
|
||||||
|
|
||||||
|
p,
|
||||||
|
ol,
|
||||||
|
ul
|
||||||
|
margin: 1em 0
|
||||||
|
|
||||||
|
th
|
||||||
|
font-weight: normal
|
||||||
|
|
||||||
|
a
|
||||||
|
cursor: pointer
|
||||||
|
color: $main-color
|
||||||
|
text-decoration: none
|
||||||
|
transition: color 0.1s linear
|
||||||
|
&.inactive
|
||||||
|
color: $inactive-link-color
|
||||||
|
cursor: default
|
||||||
|
&.icon
|
||||||
|
color: $inactive-link-color
|
||||||
|
opacity: .5
|
||||||
|
&:focus
|
||||||
|
outline: 2px solid $main-color
|
||||||
|
.vim-nav-hint
|
||||||
|
position: absolute
|
||||||
|
visibility: hidden
|
||||||
|
.darktheme a
|
||||||
|
&.inactive
|
||||||
|
color: $inactive-link-color-darktheme
|
||||||
|
&.icon
|
||||||
|
color: $inactive-link-color-darktheme
|
||||||
|
|
||||||
|
a.append, span.append
|
||||||
|
margin-left: 1em
|
||||||
|
form .fa-question-circle-o
|
||||||
|
font-size: 110%
|
||||||
|
vertical-align: middle
|
||||||
|
|
||||||
|
#content-holder
|
||||||
|
padding: 1.5em
|
||||||
|
text-align: center
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
padding: 1em
|
||||||
|
>.content-wrapper
|
||||||
|
box-sizing: border-box /* make max-width: 100% on this element include padding */
|
||||||
|
text-align: left
|
||||||
|
display: inline-block
|
||||||
|
margin: 0 auto
|
||||||
|
>*:first-child, form h1
|
||||||
|
margin-top: 0
|
||||||
|
nav.buttons
|
||||||
|
ul
|
||||||
|
display: block
|
||||||
|
max-width: 100%
|
||||||
|
white-space: nowrap
|
||||||
|
overflow-x: auto
|
||||||
|
&::-webkit-scrollbar
|
||||||
|
height: 6px
|
||||||
|
background-color: $scrollbar-bg-color
|
||||||
|
&::-webkit-scrollbar-thumb
|
||||||
|
background-color: $scrollbar-thumb-color
|
||||||
|
>.content-wrapper:not(.transparent)
|
||||||
|
background: $top-navigation-color
|
||||||
|
padding: 1.8em
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
padding: 1.5em
|
||||||
|
.content,
|
||||||
|
.content .subcontent
|
||||||
|
>*:last-child
|
||||||
|
margin-bottom: 0
|
||||||
|
|
||||||
|
.darktheme #content-holder
|
||||||
|
>.content-wrapper:not(.transparent)
|
||||||
|
background: $top-navigation-color-darktheme
|
||||||
|
|
||||||
|
hr
|
||||||
|
border: 0
|
||||||
|
border-top: 1px solid $line-color
|
||||||
|
margin: 1em 0
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
.darktheme hr
|
||||||
|
border-top: 1px solid darken($line-color, 25%)
|
||||||
|
|
||||||
|
nav
|
||||||
|
ul
|
||||||
|
list-style-type: none
|
||||||
|
padding: 0
|
||||||
|
margin: 0
|
||||||
|
display: inline-block
|
||||||
|
li
|
||||||
|
display: block
|
||||||
|
padding: 0
|
||||||
|
margin: 0
|
||||||
|
a
|
||||||
|
display: inline-block
|
||||||
|
img
|
||||||
|
margin: 0
|
||||||
|
vertical-align: top /* fix ghost margin under the image */
|
||||||
|
|
||||||
|
&.buttons
|
||||||
|
margin: 1em 0
|
||||||
|
line-height: 2.3em
|
||||||
|
vertical-align: middle
|
||||||
|
ul
|
||||||
|
li
|
||||||
|
display: inline-block
|
||||||
|
li a
|
||||||
|
padding: 0 1.2em
|
||||||
|
li:not(.active) a
|
||||||
|
color: $inactive-tab-text-color
|
||||||
|
li:hover:not(.active) a
|
||||||
|
color: $active-tab-text-color
|
||||||
|
li.active a
|
||||||
|
background: $active-tab-background-color
|
||||||
|
color: $active-tab-text-color
|
||||||
|
:focus
|
||||||
|
background: $focused-tab-background-color
|
||||||
|
outline: 0
|
||||||
|
|
||||||
|
&#top-navigation
|
||||||
|
background: $top-navigation-color
|
||||||
|
margin: 0
|
||||||
|
ul
|
||||||
|
display: block
|
||||||
|
text-align: right
|
||||||
|
li
|
||||||
|
display: inline-block
|
||||||
|
float: left
|
||||||
|
a
|
||||||
|
padding: 0 1.5em
|
||||||
|
#mobile-navigation-toggle
|
||||||
|
display: none
|
||||||
|
width: 100%
|
||||||
|
padding: 0 1em
|
||||||
|
line-height: 2.3em
|
||||||
|
font-family: inherit
|
||||||
|
border: none
|
||||||
|
background: none
|
||||||
|
color: $active-tab-text-color
|
||||||
|
.site-name
|
||||||
|
display: block
|
||||||
|
float: left
|
||||||
|
max-width: 50vw
|
||||||
|
overflow: hidden
|
||||||
|
text-overflow: ellipsis
|
||||||
|
.toggle-icon
|
||||||
|
display: block
|
||||||
|
float: right
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
text-align: left
|
||||||
|
li
|
||||||
|
display: none
|
||||||
|
float: none
|
||||||
|
a
|
||||||
|
display: block
|
||||||
|
padding: 0 1em
|
||||||
|
#mobile-navigation-toggle
|
||||||
|
display: block
|
||||||
|
&.opened
|
||||||
|
li
|
||||||
|
display: block
|
||||||
|
ul li[data-name=account],
|
||||||
|
ul li[data-name=register],
|
||||||
|
ul li[data-name=login],
|
||||||
|
ul li[data-name=logout],
|
||||||
|
ul li[data-name=settings],
|
||||||
|
ul li[data-name=help]
|
||||||
|
float: none
|
||||||
|
.access-key
|
||||||
|
text-decoration: underline
|
||||||
|
.thumbnail
|
||||||
|
width: 1.5em
|
||||||
|
height: 1.5em
|
||||||
|
margin: calc((2.3em - 1.5em) / 2)
|
||||||
|
margin-right: 0.6em
|
||||||
|
margin-left: calc(0.6em - 1.2em)
|
||||||
|
float: left
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.darktheme nav
|
||||||
|
&.buttons
|
||||||
|
ul
|
||||||
|
li:not(.active) a
|
||||||
|
color: $inactive-tab-text-color-darktheme
|
||||||
|
li:hover:not(.active) a
|
||||||
|
color: $active-tab-text-color-darktheme
|
||||||
|
li.active a
|
||||||
|
background: $active-tab-background-color-darktheme
|
||||||
|
color: $active-tab-text-color-darktheme
|
||||||
|
:focus
|
||||||
|
background: $focused-tab-background-color-darktheme
|
||||||
|
&#top-navigation
|
||||||
|
background: $top-navigation-color-darktheme
|
||||||
|
ul
|
||||||
|
#mobile-navigation-toggle
|
||||||
|
color: $text-color-darktheme
|
||||||
|
|
||||||
|
a .access-key
|
||||||
|
text-decoration: underline
|
||||||
|
|
||||||
|
.messages
|
||||||
|
margin: 0 auto
|
||||||
|
text-align: left
|
||||||
|
.message
|
||||||
|
box-sizing: border-box
|
||||||
|
width: 100%
|
||||||
|
max-width: 40em
|
||||||
|
margin: 0 0 1em 0
|
||||||
|
display: inline-block
|
||||||
|
text-align: left
|
||||||
|
padding: 0.5em 1em
|
||||||
|
&.info
|
||||||
|
border: 1px solid $message-info-border-color
|
||||||
|
background: $message-info-background-color
|
||||||
|
&.error
|
||||||
|
border: 1px solid $message-error-border-color
|
||||||
|
background: $message-error-background-color
|
||||||
|
&.success
|
||||||
|
border: 1px solid $message-success-border-color
|
||||||
|
background: $message-success-background-color
|
||||||
|
|
||||||
|
.darktheme .messages
|
||||||
|
.message
|
||||||
|
&.info
|
||||||
|
border: 1px solid darken($message-info-border-color, 30%)
|
||||||
|
background: darken($message-info-background-color, 60%)
|
||||||
|
&.error
|
||||||
|
border: 1px solid darken($message-error-border-color, 30%)
|
||||||
|
background: darken($message-error-background-color, 60%)
|
||||||
|
&.success
|
||||||
|
border: 1px solid darken($message-success-border-color, 30%)
|
||||||
|
background: darken($message-success-background-color, 80%)
|
||||||
|
|
||||||
|
.thumbnail
|
||||||
|
/*background-image: attr(data-src url)*/ /* not available yet */
|
||||||
|
vertical-align: middle
|
||||||
|
background-repeat: no-repeat
|
||||||
|
background-size: cover
|
||||||
|
background-position: center
|
||||||
|
display: inline-block
|
||||||
|
width: 20px
|
||||||
|
height: 20px
|
||||||
|
&.empty
|
||||||
|
background-image:
|
||||||
|
linear-gradient(45deg, $transparency-grid-square-color 25%, transparent 25%),
|
||||||
|
linear-gradient(-45deg, $transparency-grid-square-color 25%, transparent 25%),
|
||||||
|
linear-gradient(45deg, transparent 75%, $transparency-grid-square-color 75%),
|
||||||
|
linear-gradient(-45deg, transparent 75%, $transparency-grid-square-color 75%)
|
||||||
|
background-position: 0 0, 0 10px, 10px -10px, -10px 0px
|
||||||
|
background-repeat: repeat
|
||||||
|
background-size: 20px 20px
|
||||||
|
img
|
||||||
|
opacity: 0
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
video
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
.flexbox-dummy
|
||||||
|
height: 0 !important
|
||||||
|
padding-top: 0 !important
|
||||||
|
padding-bottom: 0 !important
|
||||||
|
margin-top: 0 !important
|
||||||
|
margin-bottom: 0 !important
|
||||||
|
|
||||||
|
.table-wrap
|
||||||
|
overflow-x: auto
|
||||||
|
&::-webkit-scrollbar
|
||||||
|
height: 6px
|
||||||
|
background-color: $scrollbar-bg-color
|
||||||
|
&::-webkit-scrollbar-thumb
|
||||||
|
background-color: $scrollbar-thumb-color
|
||||||
|
|
||||||
|
/* hack to prevent text from being copied */
|
||||||
|
[data-pseudo-content]:before {
|
||||||
|
content: attr(data-pseudo-content)
|
||||||
|
}
|
32
client/css/expander-control.styl
Normal file
32
client/css/expander-control.styl
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
.expander
|
||||||
|
&.collapsed
|
||||||
|
margin-bottom: 1em
|
||||||
|
&>*
|
||||||
|
display: none
|
||||||
|
&>header
|
||||||
|
display: block
|
||||||
|
header
|
||||||
|
background: $active-tab-background-color
|
||||||
|
line-height: 2em
|
||||||
|
a
|
||||||
|
padding: 0 0.5em
|
||||||
|
display: block
|
||||||
|
color: mix($text-color, $inactive-link-color)
|
||||||
|
font-size: 120%
|
||||||
|
i
|
||||||
|
font-size: 1em
|
||||||
|
color: $inactive-link-color
|
||||||
|
float: right
|
||||||
|
line-height: 2em
|
||||||
|
.expander-content
|
||||||
|
padding: 0.5em 0.5em 2em 0.5em
|
||||||
|
|
||||||
|
.darktheme .expander
|
||||||
|
header
|
||||||
|
background: $active-tab-background-color-darktheme
|
||||||
|
a
|
||||||
|
color: mix($text-color-darktheme, $inactive-link-color-darktheme)
|
||||||
|
i
|
||||||
|
color: $inactive-link-color-darktheme
|
38
client/css/help-view.styl
Normal file
38
client/css/help-view.styl
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
#help
|
||||||
|
width: 100%
|
||||||
|
max-width: 45em
|
||||||
|
nav
|
||||||
|
margin-bottom: 1.5em
|
||||||
|
td, th
|
||||||
|
padding: 0 0.5em
|
||||||
|
&:first-child
|
||||||
|
white-space: pre
|
||||||
|
.section
|
||||||
|
margin-top: 2em
|
||||||
|
h1
|
||||||
|
margin-top: 2.5em
|
||||||
|
font-size: 1.6em
|
||||||
|
&:first-child
|
||||||
|
margin-top: 0
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
margin-top: 1.5em
|
||||||
|
&:first-child
|
||||||
|
margin-top: 0
|
||||||
|
nav
|
||||||
|
ul
|
||||||
|
margin: 0 auto
|
||||||
|
text-align: left
|
||||||
|
nav.secondary
|
||||||
|
font-size: 0.95em
|
||||||
|
|
||||||
|
@media (max-width: 600px)
|
||||||
|
th, thead
|
||||||
|
display: none
|
||||||
|
table, tr, td, tbody
|
||||||
|
display: block
|
||||||
|
tr
|
||||||
|
margin-bottom: 0.8em
|
||||||
|
pre
|
||||||
|
white-space: pre-wrap
|
67
client/css/home-view.styl
Normal file
67
client/css/home-view.styl
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#home
|
||||||
|
text-align: center !important
|
||||||
|
max-width: 100%
|
||||||
|
|
||||||
|
header
|
||||||
|
margin-bottom: 1em
|
||||||
|
h1
|
||||||
|
line-height: initial
|
||||||
|
font-size: 2.5em
|
||||||
|
margin: 0
|
||||||
|
|
||||||
|
.messages
|
||||||
|
text-align: center
|
||||||
|
.message
|
||||||
|
margin: 0 auto 2em auto
|
||||||
|
|
||||||
|
form
|
||||||
|
display: inline-block
|
||||||
|
width: auto
|
||||||
|
vertical-align: middle
|
||||||
|
margin: 0 0 2em 0
|
||||||
|
text-align: left
|
||||||
|
white-space: nowrap
|
||||||
|
input
|
||||||
|
width: auto
|
||||||
|
.sep
|
||||||
|
margin: 0 0.75em
|
||||||
|
@media (max-width: 500px)
|
||||||
|
.sep, a
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.post-container
|
||||||
|
margin-bottom: 2em
|
||||||
|
display: flex
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
&:empty
|
||||||
|
margin-bottom: 0
|
||||||
|
|
||||||
|
nav
|
||||||
|
a
|
||||||
|
padding: 0.5em
|
||||||
|
|
||||||
|
aside
|
||||||
|
margin-bottom: 0.3em
|
||||||
|
font-size: 90%
|
||||||
|
white-space: nowrap
|
||||||
|
|
||||||
|
footer
|
||||||
|
line-height: 150%
|
||||||
|
font-size: 80%
|
||||||
|
ul
|
||||||
|
padding: 0
|
||||||
|
text-align: center
|
||||||
|
li
|
||||||
|
display: inline
|
||||||
|
white-space: nowrap
|
||||||
|
@media (max-width: 800px)
|
||||||
|
display: block
|
||||||
|
.sep
|
||||||
|
word-spacing: 1.1em
|
||||||
|
background-repeat: no-repeat
|
||||||
|
background-position: 50% 50%
|
||||||
|
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12'><circle cx='6' cy='6' r='2' fill='%23000000'/></svg>")
|
||||||
|
|
||||||
|
.thumbnail
|
||||||
|
margin-right: 0.4em
|
6
client/css/mixins.styl
Normal file
6
client/css/mixins.styl
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
unselectable()
|
||||||
|
-webkit-user-select: none
|
||||||
|
-moz-user-select: none
|
||||||
|
-ms-user-select: none
|
||||||
|
-o-user-select: none
|
||||||
|
user-select: none
|
23
client/css/nprogress.styl
Normal file
23
client/css/nprogress.styl
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
#nprogress
|
||||||
|
pointer-events: none
|
||||||
|
|
||||||
|
.bar
|
||||||
|
background: $main-color
|
||||||
|
position: fixed
|
||||||
|
z-index: 1031
|
||||||
|
top: 0
|
||||||
|
left: 0
|
||||||
|
width: 100%
|
||||||
|
height: 2px
|
||||||
|
|
||||||
|
.spinner-icon
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.peg
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.nprogress-custom-parent
|
||||||
|
overflow: hidden
|
||||||
|
position: relative
|
35
client/css/pager.styl
Normal file
35
client/css/pager.styl
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
.pager
|
||||||
|
nav
|
||||||
|
.disabled
|
||||||
|
opacity: .5
|
||||||
|
|
||||||
|
.page
|
||||||
|
position: relative
|
||||||
|
.page-header
|
||||||
|
margin: 0.5em 0
|
||||||
|
position: relative
|
||||||
|
&:before
|
||||||
|
display: block
|
||||||
|
content: ''
|
||||||
|
position: absolute
|
||||||
|
left: 0
|
||||||
|
top: 50%
|
||||||
|
right: 0
|
||||||
|
height: 3px
|
||||||
|
background: $top-navigation-color
|
||||||
|
z-index: 1
|
||||||
|
span
|
||||||
|
position: relative
|
||||||
|
background: $window-color
|
||||||
|
padding: 0 1em
|
||||||
|
z-index: 2
|
||||||
|
|
||||||
|
.darktheme .pager
|
||||||
|
.page
|
||||||
|
.page-header
|
||||||
|
&:before
|
||||||
|
background: $top-navigation-color-darktheme
|
||||||
|
span
|
||||||
|
background: $window-color-darktheme
|
2
client/css/password-reset.styl
Normal file
2
client/css/password-reset.styl
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#password-reset
|
||||||
|
max-width: 30em
|
29
client/css/pool-categories-view.styl
Normal file
29
client/css/pool-categories-view.styl
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
.content-wrapper.pool-categories
|
||||||
|
width: 100%
|
||||||
|
max-width: 45em
|
||||||
|
table
|
||||||
|
border-spacing: 0
|
||||||
|
width: 100%
|
||||||
|
tr.default td
|
||||||
|
background: $default-pool-category-background-color
|
||||||
|
td, th
|
||||||
|
padding: .4em
|
||||||
|
&.color
|
||||||
|
input[type=text]
|
||||||
|
width: 8em
|
||||||
|
&.usages
|
||||||
|
text-align: center
|
||||||
|
&.remove, &.set-default
|
||||||
|
white-space: pre
|
||||||
|
th
|
||||||
|
white-space: nowrap
|
||||||
|
&:first-child
|
||||||
|
padding-left: 0
|
||||||
|
&:last-child
|
||||||
|
padding-right: 0
|
||||||
|
tfoot
|
||||||
|
display: none
|
||||||
|
form
|
||||||
|
width: auto
|
58
client/css/pool-input-control.styl
Normal file
58
client/css/pool-input-control.styl
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
div.pool-input
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
.main-control
|
||||||
|
display: flex
|
||||||
|
input
|
||||||
|
flex: 5
|
||||||
|
button
|
||||||
|
flex: 1
|
||||||
|
margin: 0 0 0 0.5em
|
||||||
|
|
||||||
|
|
||||||
|
ul.compact-pools
|
||||||
|
width: 100%
|
||||||
|
margin: 0.5em 0 0 0
|
||||||
|
padding: 0
|
||||||
|
li
|
||||||
|
margin: 0
|
||||||
|
width: 100%
|
||||||
|
line-height: 140%
|
||||||
|
white-space: nowrap
|
||||||
|
overflow: hidden
|
||||||
|
text-overflow: ellipsis
|
||||||
|
transition: background-color 0.5s linear
|
||||||
|
a
|
||||||
|
display: inline
|
||||||
|
a:focus
|
||||||
|
outline: 0
|
||||||
|
box-shadow: inset 0 0 0 2px $main-color
|
||||||
|
&.implication
|
||||||
|
background: $implied-pool-background-color
|
||||||
|
color: $implied-pool-text-color
|
||||||
|
&.new
|
||||||
|
background: $new-pool-background-color
|
||||||
|
color: $new-pool-text-color
|
||||||
|
&.duplicate
|
||||||
|
background: $duplicate-pool-background-color
|
||||||
|
color: $duplicate-pool-text-color
|
||||||
|
i
|
||||||
|
padding-right: 0.4em
|
||||||
|
|
||||||
|
div.pool-input, ul.compact-pools
|
||||||
|
.pool-usages, .pool-weight, .remove-pool
|
||||||
|
color: $inactive-link-color
|
||||||
|
unselectable()
|
||||||
|
.pool-usages, .pool-weight
|
||||||
|
font-size: 90%
|
||||||
|
.pool-usages, .pool-weight
|
||||||
|
margin-left: 0.7em
|
||||||
|
.remove-pool
|
||||||
|
margin-right: 0.5em
|
||||||
|
|
||||||
|
.darktheme
|
||||||
|
div.pool-input, ul.compact-pools
|
||||||
|
.pool-usages, .pool-weight, .remove-pool
|
||||||
|
color: $inactive-link-color-darktheme
|
63
client/css/pool-list-view.styl
Normal file
63
client/css/pool-list-view.styl
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
.pool-list
|
||||||
|
table
|
||||||
|
width: 100%
|
||||||
|
border-spacing: 0
|
||||||
|
text-align: left
|
||||||
|
line-height: 1.3em
|
||||||
|
tr:hover td
|
||||||
|
background: $top-navigation-color
|
||||||
|
th, td
|
||||||
|
padding: 0.1em 0.5em
|
||||||
|
th
|
||||||
|
white-space: nowrap
|
||||||
|
background: $top-navigation-color
|
||||||
|
.names
|
||||||
|
width: 84%
|
||||||
|
.post-count
|
||||||
|
text-align: center
|
||||||
|
width: 8%
|
||||||
|
.creation-time
|
||||||
|
text-align: center
|
||||||
|
width: 8%
|
||||||
|
white-space: pre
|
||||||
|
ul
|
||||||
|
list-style-type: none
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
display: inline
|
||||||
|
li
|
||||||
|
padding: 0
|
||||||
|
display: inline
|
||||||
|
&:not(:last-child):after
|
||||||
|
content: ', '
|
||||||
|
@media (max-width: 800px)
|
||||||
|
.posts
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.darktheme .pool-list
|
||||||
|
table
|
||||||
|
tr:hover td
|
||||||
|
background: $top-navigation-color-darktheme
|
||||||
|
th
|
||||||
|
background: $top-navigation-color-darktheme
|
||||||
|
|
||||||
|
.pool-list-header
|
||||||
|
label
|
||||||
|
display: none !important
|
||||||
|
text-align: left
|
||||||
|
form
|
||||||
|
width: auto
|
||||||
|
input[name=search-text]
|
||||||
|
width: 25em
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
width: 100%
|
||||||
|
.append
|
||||||
|
vertical-align: middle
|
||||||
|
font-size: 0.95em
|
||||||
|
color: $inactive-link-color
|
||||||
|
|
||||||
|
.darktheme .pool-list-header
|
||||||
|
.append
|
||||||
|
color: $inactive-link-color-darktheme
|
33
client/css/pool-view.styl
Normal file
33
client/css/pool-view.styl
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#pool
|
||||||
|
width: 100%
|
||||||
|
max-width: 40em
|
||||||
|
h1
|
||||||
|
word-break: break-all
|
||||||
|
line-height: 130%
|
||||||
|
margin-top: 0
|
||||||
|
form
|
||||||
|
width: 100%
|
||||||
|
.pool-edit
|
||||||
|
textarea
|
||||||
|
height: 10em
|
||||||
|
.pool-summary
|
||||||
|
section
|
||||||
|
&.description
|
||||||
|
margin: 1.5em 0 0 0
|
||||||
|
&.details
|
||||||
|
vertical-align: top
|
||||||
|
padding-right: 0.5em
|
||||||
|
ul
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
list-style-type: none
|
||||||
|
li
|
||||||
|
display: inline
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
li:not(:last-of-type):after
|
||||||
|
content: ', '
|
||||||
|
ul:empty:after
|
||||||
|
content: '(none)'
|
||||||
|
section
|
||||||
|
margin-bottom: 1em
|
29
client/css/post-content-control.styl
Normal file
29
client/css/post-content-control.styl
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
.post-container
|
||||||
|
.post-content.transparency-grid img
|
||||||
|
background-image:
|
||||||
|
linear-gradient(45deg, $transparency-grid-square-color 25%, transparent 25%),
|
||||||
|
linear-gradient(-45deg, $transparency-grid-square-color 25%, transparent 25%),
|
||||||
|
linear-gradient(45deg, transparent 75%, $transparency-grid-square-color 75%),
|
||||||
|
linear-gradient(-45deg, transparent 75%, $transparency-grid-square-color 75%)
|
||||||
|
background-size: 20px 20px
|
||||||
|
background-position: 0 0, 0 10px, 10px -10px, -10px 0px
|
||||||
|
|
||||||
|
text-align: center
|
||||||
|
.post-content
|
||||||
|
text-align: left
|
||||||
|
margin: 0 auto
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
.resize-listener
|
||||||
|
position: absolute
|
||||||
|
left: 0
|
||||||
|
right: 0
|
||||||
|
top: 0
|
||||||
|
bottom: 0
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
img
|
||||||
|
image-orientation: from-image
|
37
client/css/post-detail-view.styl
Normal file
37
client/css/post-detail-view.styl
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#post
|
||||||
|
width: 100%
|
||||||
|
max-width: 40em
|
||||||
|
h1
|
||||||
|
margin-top: 0
|
||||||
|
form
|
||||||
|
width: 100%
|
||||||
|
.buttons i
|
||||||
|
margin-right: 0.5em
|
||||||
|
.post-merge
|
||||||
|
.left-post-container
|
||||||
|
width: 47%
|
||||||
|
float: left
|
||||||
|
.right-post-container
|
||||||
|
width: 47%
|
||||||
|
float: right
|
||||||
|
.post-mirror
|
||||||
|
margin-bottom: 1em
|
||||||
|
&:after
|
||||||
|
display: block
|
||||||
|
height: 1px
|
||||||
|
content: ' '
|
||||||
|
clear: both
|
||||||
|
.post-thumbnail .thumbnail
|
||||||
|
width: 100%
|
||||||
|
height: 9em
|
||||||
|
.target-post .thumbnail
|
||||||
|
margin-right: 0.35em
|
||||||
|
.target-post, .target-post-content
|
||||||
|
margin: 1em 0
|
||||||
|
header
|
||||||
|
margin-bottom: 1em
|
||||||
|
label
|
||||||
|
display: inline-block
|
||||||
|
margin-top: 2px
|
||||||
|
input[type=text]
|
||||||
|
width: 6em
|
273
client/css/post-list-view.styl
Normal file
273
client/css/post-list-view.styl
Normal file
|
@ -0,0 +1,273 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
.post-list
|
||||||
|
ul
|
||||||
|
list-style-type: none
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
display: flex
|
||||||
|
align-content: flex-end
|
||||||
|
flex-wrap: wrap
|
||||||
|
margin: 0 -0.25em
|
||||||
|
|
||||||
|
li
|
||||||
|
position: relative
|
||||||
|
flex-grow: 1
|
||||||
|
margin: 0 0.25em 0.5em 0.25em
|
||||||
|
display: inline-block
|
||||||
|
text-align: left
|
||||||
|
min-width: 10em
|
||||||
|
width: 12vw
|
||||||
|
&:not(.flexbox-dummy)
|
||||||
|
min-height: 7.5em
|
||||||
|
height: 9vw
|
||||||
|
|
||||||
|
.thumbnail-wrapper
|
||||||
|
display: inline-block
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
line-height: 80%
|
||||||
|
font-size: 80%
|
||||||
|
color: white
|
||||||
|
outline-offset: -3px
|
||||||
|
box-shadow: 0 0 0 1px rgba(0,0,0,0.2)
|
||||||
|
|
||||||
|
.type, .stats
|
||||||
|
position: absolute
|
||||||
|
bottom: 0.5em
|
||||||
|
padding: 0.33em 0.5em
|
||||||
|
background: rgba(0,0,0,0.5)
|
||||||
|
height: 1em
|
||||||
|
|
||||||
|
.type
|
||||||
|
float: left
|
||||||
|
left: 0.5em
|
||||||
|
&[data-type=image]
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.stats
|
||||||
|
float: right
|
||||||
|
right: 0.5em
|
||||||
|
text-align: right
|
||||||
|
i
|
||||||
|
margin-right: 0.25em
|
||||||
|
.icon:not(:first-of-type)
|
||||||
|
margin-left: 1em
|
||||||
|
|
||||||
|
.edit-overlay
|
||||||
|
position: absolute
|
||||||
|
top: 0.5em
|
||||||
|
left: 0.5em
|
||||||
|
|
||||||
|
.tag-flipper
|
||||||
|
display: inline-block
|
||||||
|
padding: 0.5em
|
||||||
|
box-sizing: border-box
|
||||||
|
border: 0
|
||||||
|
&:after
|
||||||
|
display: inline-block
|
||||||
|
width: 1em
|
||||||
|
height: 1em
|
||||||
|
text-align: center
|
||||||
|
line-height: 1em
|
||||||
|
font-size: 2.2em
|
||||||
|
&.tagged
|
||||||
|
background: rgba(0, 230, 0, 0.7)
|
||||||
|
&:after
|
||||||
|
color: white
|
||||||
|
content: '-'
|
||||||
|
&:not(.tagged)
|
||||||
|
background: rgba(255, 0, 0, 0.7)
|
||||||
|
&:after
|
||||||
|
color: white
|
||||||
|
content: '+'
|
||||||
|
&[data-disabled]
|
||||||
|
background: rgba(200, 200, 200, 0.7)
|
||||||
|
|
||||||
|
.safety-flipper a
|
||||||
|
display: inline-block
|
||||||
|
margin: 0.1em
|
||||||
|
box-sizing: border-box
|
||||||
|
border: 0
|
||||||
|
display: inline-block
|
||||||
|
width: 1.2em
|
||||||
|
height: 1.2em
|
||||||
|
text-align: center
|
||||||
|
line-height: 1em
|
||||||
|
font-size: 1.6em
|
||||||
|
border: 3px solid
|
||||||
|
&.safety-safe
|
||||||
|
background-color: darken($safety-safe, 5%)
|
||||||
|
border-color: @background-color
|
||||||
|
&:not(.active)
|
||||||
|
background-color: alpha(@background-color, 0.3)
|
||||||
|
&.safety-sketchy
|
||||||
|
background-color: $safety-sketchy
|
||||||
|
border-color: @background-color
|
||||||
|
&:not(.active)
|
||||||
|
background-color: alpha(@background-color, 0.3)
|
||||||
|
&.safety-unsafe
|
||||||
|
background-color: $safety-unsafe
|
||||||
|
border-color: @background-color
|
||||||
|
&:not(.active)
|
||||||
|
background-color: alpha(@background-color, 0.3)
|
||||||
|
&[data-disabled]
|
||||||
|
background: rgba(200, 200, 200, 0.7)
|
||||||
|
|
||||||
|
.delete-flipper
|
||||||
|
display: inline-block
|
||||||
|
padding: 0.5em
|
||||||
|
box-sizing: border-box
|
||||||
|
border: 0
|
||||||
|
&:after
|
||||||
|
display: inline-block
|
||||||
|
width: 1em
|
||||||
|
height: 1em
|
||||||
|
text-align: center
|
||||||
|
line-height: 1em
|
||||||
|
font-size: 2.2em
|
||||||
|
&.delete
|
||||||
|
background: rgba(255, 0, 0, 0.7)
|
||||||
|
&:after
|
||||||
|
color: white
|
||||||
|
font-family: FontAwesome;
|
||||||
|
content: "\f1f8"; // fa-trash
|
||||||
|
&:not(.delete)
|
||||||
|
background: rgba(200, 200, 200, 0.7)
|
||||||
|
&:after
|
||||||
|
color: white
|
||||||
|
content: '-'
|
||||||
|
|
||||||
|
.thumbnail
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
outline-offset: -3px
|
||||||
|
&:not(.empty)
|
||||||
|
background-position: 50% 30%
|
||||||
|
|
||||||
|
.thumbnail-wrapper.no-tags
|
||||||
|
.thumbnail
|
||||||
|
outline: 4px solid $post-thumbnail-no-tags-border-color
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background: $post-thumbnail-border-color
|
||||||
|
.thumbnail
|
||||||
|
opacity: .9
|
||||||
|
|
||||||
|
&:hover a, a:active, a:focus
|
||||||
|
.thumbnail
|
||||||
|
outline: 4px solid $main-color !important
|
||||||
|
|
||||||
|
.post-flow
|
||||||
|
ul
|
||||||
|
li
|
||||||
|
min-width: inherit
|
||||||
|
width: inherit
|
||||||
|
&:not(.flexbox-dummy)
|
||||||
|
height: 14vw
|
||||||
|
.thumbnail
|
||||||
|
outline-offset: -1px
|
||||||
|
.thumbnail-wrapper.no-tags
|
||||||
|
.thumbnail
|
||||||
|
outline: 2px solid $post-thumbnail-no-tags-border-color
|
||||||
|
&:hover a, a:active, a:focus
|
||||||
|
.thumbnail
|
||||||
|
outline: 2px solid $main-color !important
|
||||||
|
|
||||||
|
.post-list-header
|
||||||
|
white-space: nowrap
|
||||||
|
text-align: left
|
||||||
|
|
||||||
|
label
|
||||||
|
display: none !important
|
||||||
|
form
|
||||||
|
width: auto
|
||||||
|
margin-bottom: 0.75em
|
||||||
|
*
|
||||||
|
vertical-align: top
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
display: block
|
||||||
|
&.bulk-edit-tags:not(.opened), &.bulk-edit-safety:not(.opened)
|
||||||
|
float: left
|
||||||
|
margin-right: 1em
|
||||||
|
input
|
||||||
|
margin-bottom: 0.25em
|
||||||
|
margin-right: 0.25em
|
||||||
|
input[name=search-text]
|
||||||
|
width: 25em
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
display: block
|
||||||
|
width: 100%
|
||||||
|
margin-bottom: 0.5em
|
||||||
|
.append
|
||||||
|
vertical-align: middle
|
||||||
|
font-size: 0.95em
|
||||||
|
color: $inactive-link-color
|
||||||
|
.bulk-edit
|
||||||
|
&:not(.opened)
|
||||||
|
.close
|
||||||
|
display: none
|
||||||
|
&.opened
|
||||||
|
.open
|
||||||
|
display: none
|
||||||
|
&.hidden
|
||||||
|
display: none
|
||||||
|
.bulk-edit-tags
|
||||||
|
&.opened
|
||||||
|
.hint
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
display: block
|
||||||
|
margin-bottom: 0.5em
|
||||||
|
&:not(.opened)
|
||||||
|
[type=text],
|
||||||
|
.start
|
||||||
|
display: none
|
||||||
|
.hint
|
||||||
|
display: none
|
||||||
|
input[name=tag]
|
||||||
|
width: 24em
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
display: block
|
||||||
|
width: 100%
|
||||||
|
margin-bottom: 0.5em
|
||||||
|
.append
|
||||||
|
&.open,
|
||||||
|
&.hint
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
margin-left: 0
|
||||||
|
.hint
|
||||||
|
margin-right: 1em
|
||||||
|
.bulk-edit-safety
|
||||||
|
.append
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
margin-left: 0
|
||||||
|
.bulk-edit-delete
|
||||||
|
&.opened
|
||||||
|
.start
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
margin-left: 0
|
||||||
|
&:not(.opened)
|
||||||
|
.start
|
||||||
|
display: none
|
||||||
|
.append.open
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
margin-left: 0
|
||||||
|
.start
|
||||||
|
margin-left: 1em
|
||||||
|
.safety
|
||||||
|
margin-right: 0.25em
|
||||||
|
&.safety-safe
|
||||||
|
background-color: $safety-safe
|
||||||
|
border-color: @background-color
|
||||||
|
&.disabled
|
||||||
|
background-color: alpha(@background-color, 0.15)
|
||||||
|
&.safety-sketchy
|
||||||
|
background-color: $safety-sketchy
|
||||||
|
border-color: @background-color
|
||||||
|
&.disabled
|
||||||
|
background-color: alpha(@background-color, 0.15)
|
||||||
|
&.safety-unsafe
|
||||||
|
background-color: $safety-unsafe
|
||||||
|
border-color: @background-color
|
||||||
|
&.disabled
|
||||||
|
background-color: alpha(@background-color, 0.15)
|
178
client/css/post-main-view.styl
Normal file
178
client/css/post-main-view.styl
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
.post-view
|
||||||
|
width: 100%
|
||||||
|
display: flex !important
|
||||||
|
flex-direction: row
|
||||||
|
|
||||||
|
>.sidebar
|
||||||
|
margin-right: 1em
|
||||||
|
min-width: 21em
|
||||||
|
max-width: 21em
|
||||||
|
line-height: 160%
|
||||||
|
|
||||||
|
a:active
|
||||||
|
border: 0
|
||||||
|
outline: 0
|
||||||
|
|
||||||
|
>.sidebar>nav.buttons, >.content nav.buttons
|
||||||
|
margin-top: 0
|
||||||
|
display: flex
|
||||||
|
flex-wrap: wrap
|
||||||
|
article
|
||||||
|
flex: 1 0 33%
|
||||||
|
a
|
||||||
|
display: inline-block
|
||||||
|
width: 100%
|
||||||
|
padding: 0.3em 0
|
||||||
|
text-align: center
|
||||||
|
vertical-align: middle
|
||||||
|
transition: background 0.2s linear, box-shadow 0.2s linear
|
||||||
|
&:not(.inactive):hover
|
||||||
|
background: lighten($main-color, 90%)
|
||||||
|
i
|
||||||
|
font-size: 140%
|
||||||
|
text-align: center
|
||||||
|
@media (max-width: 800px)
|
||||||
|
margin-top: 0.6em
|
||||||
|
margin-bottom: 0.6em
|
||||||
|
|
||||||
|
>.content
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.post-container
|
||||||
|
margin-bottom: 0.6em
|
||||||
|
|
||||||
|
.post-content
|
||||||
|
margin: 0
|
||||||
|
|
||||||
|
.after-mobile-controls
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.darktheme .post-view
|
||||||
|
>.sidebar, >.content
|
||||||
|
nav.buttons
|
||||||
|
article
|
||||||
|
a:not(.inactive):hover
|
||||||
|
background: unset
|
||||||
|
box-shadow: inset 0 0 0 0.3em $main-color
|
||||||
|
|
||||||
|
@media (max-width: 800px)
|
||||||
|
.post-view
|
||||||
|
flex-wrap: wrap
|
||||||
|
>.after-mobile-controls
|
||||||
|
order: 3
|
||||||
|
>.sidebar
|
||||||
|
order: 2
|
||||||
|
min-width: 100%
|
||||||
|
max-width: 0
|
||||||
|
margin-right: 0
|
||||||
|
>.content
|
||||||
|
order: 1
|
||||||
|
|
||||||
|
|
||||||
|
.post-view .readonly-sidebar
|
||||||
|
.details
|
||||||
|
i
|
||||||
|
margin-right: 0.6em
|
||||||
|
display: inline-block
|
||||||
|
width: 1em
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
.safety-safe
|
||||||
|
color: $safety-safe
|
||||||
|
.safety-sketchy
|
||||||
|
color: $safety-sketchy
|
||||||
|
.safety-unsafe
|
||||||
|
color: $safety-unsafe
|
||||||
|
|
||||||
|
.upload-info
|
||||||
|
.thumbnail
|
||||||
|
width: 1em
|
||||||
|
height: 1em
|
||||||
|
margin: -0.1em 0.6em 0 0
|
||||||
|
|
||||||
|
.zoom
|
||||||
|
margin-top: 1em
|
||||||
|
a
|
||||||
|
display: inline-block
|
||||||
|
.active
|
||||||
|
text-decoration: underline
|
||||||
|
|
||||||
|
.social
|
||||||
|
margin-top: 1em
|
||||||
|
.score-container
|
||||||
|
float: left
|
||||||
|
margin-right: 3em
|
||||||
|
.downvote i
|
||||||
|
text-align: right
|
||||||
|
i
|
||||||
|
text-align: left
|
||||||
|
margin: 0
|
||||||
|
.value
|
||||||
|
text-align: center
|
||||||
|
display: inline-block
|
||||||
|
width: 2em
|
||||||
|
|
||||||
|
.relations
|
||||||
|
margin-top: 2em
|
||||||
|
h1
|
||||||
|
margin-bottom: 0.5em
|
||||||
|
.thumbnail
|
||||||
|
width: 4em
|
||||||
|
height: 3em
|
||||||
|
li
|
||||||
|
margin: 0 0.3em 0.3em 0
|
||||||
|
display: inline-block
|
||||||
|
|
||||||
|
.tags
|
||||||
|
margin-top: 2em
|
||||||
|
h1
|
||||||
|
margin-bottom: 0.5em
|
||||||
|
|
||||||
|
.post-view .edit-sidebar
|
||||||
|
.expander-content
|
||||||
|
section:not(:last-child)
|
||||||
|
margin-bottom: 1em
|
||||||
|
|
||||||
|
.safety
|
||||||
|
&>label
|
||||||
|
width: 100%
|
||||||
|
.radio-wrapper
|
||||||
|
display: flex
|
||||||
|
flex-wrap: wrap
|
||||||
|
.radio-wrapper label
|
||||||
|
flex-grow: 1
|
||||||
|
display: inline-block
|
||||||
|
|
||||||
|
.management
|
||||||
|
ul
|
||||||
|
list-style-type: none
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
li
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
.post-source
|
||||||
|
textarea
|
||||||
|
white-space: pre
|
||||||
|
overflow-wrap: normal
|
||||||
|
overflow-x: scroll
|
||||||
|
|
||||||
|
form
|
||||||
|
width: auto
|
||||||
|
|
||||||
|
label:not(.file-dropper)
|
||||||
|
margin-bottom: 0.3em
|
||||||
|
display: block
|
||||||
|
|
||||||
|
input[type=submit],
|
||||||
|
input[type=button],
|
||||||
|
button
|
||||||
|
width: 100%
|
||||||
|
&:focus
|
||||||
|
border: 2px solid $text-color !important
|
||||||
|
|
||||||
|
.messages
|
||||||
|
margin-top: 1em
|
69
client/css/post-notes-overlay-control.styl
Normal file
69
client/css/post-notes-overlay-control.styl
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
.post-overlay
|
||||||
|
&[data-state=ready-to-draw],
|
||||||
|
&[data-state=drawing-rectangle],
|
||||||
|
&[data-state=drawing-polygon]
|
||||||
|
&:after
|
||||||
|
box-sizing: border-box
|
||||||
|
border: 0.3em dashed $active-note-overlay-border-color
|
||||||
|
background: $active-note-overlay-background-color
|
||||||
|
display: block
|
||||||
|
content: ' '
|
||||||
|
pointer-events: none
|
||||||
|
position: absolute
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
left: 0
|
||||||
|
right: 0
|
||||||
|
top: 0
|
||||||
|
bottom: 0
|
||||||
|
|
||||||
|
.notes-overlay
|
||||||
|
g
|
||||||
|
stroke-width: 1px
|
||||||
|
|
||||||
|
polygon
|
||||||
|
fill: $note-background-color
|
||||||
|
stroke: $note-border-color
|
||||||
|
pointer-events: auto
|
||||||
|
ellipse
|
||||||
|
display: none
|
||||||
|
|
||||||
|
g[data-state=editing], g[data-state=drawing]
|
||||||
|
stroke-width: 2px
|
||||||
|
|
||||||
|
polygon
|
||||||
|
fill: $edited-note-background-color
|
||||||
|
stroke: $edited-note-border-color
|
||||||
|
ellipse
|
||||||
|
fill: $edited-note-border-color
|
||||||
|
display: block
|
||||||
|
&.nearby
|
||||||
|
fill: $hovered-note-point-color
|
||||||
|
|
||||||
|
g[data-state=drawing]
|
||||||
|
ellipse:first-of-type
|
||||||
|
fill: $first-note-point-color
|
||||||
|
&.nearby
|
||||||
|
fill: $hovered-first-note-point-color
|
||||||
|
|
||||||
|
.note-text
|
||||||
|
position: absolute
|
||||||
|
max-width: 22.5em
|
||||||
|
display: none
|
||||||
|
|
||||||
|
&:not([data-state=read-only])
|
||||||
|
pointer-events: none
|
||||||
|
|
||||||
|
&>.wrapper
|
||||||
|
background: $note-text-background-color
|
||||||
|
padding: 0.3em 0.6em
|
||||||
|
border: 1px solid $note-text-border-color
|
||||||
|
color: $note-text-text-color
|
||||||
|
box-sizing: border-box
|
||||||
|
|
||||||
|
p:last-of-type
|
||||||
|
margin-bottom: 0
|
||||||
|
p:first-of-type
|
||||||
|
margin-top: 0
|
182
client/css/post-upload.styl
Normal file
182
client/css/post-upload.styl
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
@import colors
|
||||||
|
$upload-header-background-color = $top-navigation-color
|
||||||
|
$upload-header-background-color-darktheme = $top-navigation-color-darktheme
|
||||||
|
$upload-border-color = #DDD
|
||||||
|
$cancel-button-color = tomato
|
||||||
|
|
||||||
|
#post-upload
|
||||||
|
form
|
||||||
|
width: 100%
|
||||||
|
max-width: 40em
|
||||||
|
margin: 0 auto
|
||||||
|
text-align: left
|
||||||
|
|
||||||
|
&.inactive input[type=submit],
|
||||||
|
&.inactive .skip-duplicates
|
||||||
|
&.inactive .always-upload-similar
|
||||||
|
&.inactive .pause-remain-on-error
|
||||||
|
&.uploading input[type=submit],
|
||||||
|
&.uploading .skip-duplicates,
|
||||||
|
&.uploading .always-upload-similar
|
||||||
|
&.uploading .pause-remain-on-error
|
||||||
|
&:not(.uploading) .cancel
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.dropper-container
|
||||||
|
margin: 0 auto
|
||||||
|
.file-dropper
|
||||||
|
font-size: 150%
|
||||||
|
padding: 2em
|
||||||
|
small
|
||||||
|
font-size: 60%
|
||||||
|
|
||||||
|
input[type=submit]
|
||||||
|
margin-top: 1em
|
||||||
|
|
||||||
|
.cancel
|
||||||
|
margin-top: 1em
|
||||||
|
background: $cancel-button-color
|
||||||
|
border-color: $cancel-button-color
|
||||||
|
&:focus
|
||||||
|
border: 2px solid $text-color
|
||||||
|
|
||||||
|
.skip-duplicates
|
||||||
|
margin-left: 1em
|
||||||
|
|
||||||
|
.always-upload-similar
|
||||||
|
margin-left: 1em
|
||||||
|
|
||||||
|
.pause-remain-on-error
|
||||||
|
margin-left: 1em
|
||||||
|
|
||||||
|
form>.messages
|
||||||
|
margin-top: 1em
|
||||||
|
|
||||||
|
.uploadables-container
|
||||||
|
list-style-type: none
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
.uploadable-container
|
||||||
|
clear: both
|
||||||
|
margin: 0 0 1.2em 0
|
||||||
|
padding-left: 13em
|
||||||
|
|
||||||
|
img
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
video
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
&>.thumbnail-wrapper
|
||||||
|
float: left
|
||||||
|
width: 12em
|
||||||
|
height: 8em
|
||||||
|
margin: 0 0 0 -13em
|
||||||
|
.thumbnail
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
.uploadable
|
||||||
|
border: 1px solid $upload-border-color
|
||||||
|
min-height: 8em
|
||||||
|
box-sizing: border-box
|
||||||
|
|
||||||
|
header
|
||||||
|
line-height: 1.5em
|
||||||
|
padding: 0.25em 1em
|
||||||
|
text-align: left
|
||||||
|
background: $upload-header-background-color
|
||||||
|
border-bottom: 1px solid $upload-border-color
|
||||||
|
|
||||||
|
nav
|
||||||
|
&:first-of-type
|
||||||
|
float: left
|
||||||
|
a
|
||||||
|
margin: 0 0.5em 0 0
|
||||||
|
&:last-of-type
|
||||||
|
float: right
|
||||||
|
a
|
||||||
|
margin: 0 0 0 0.5em
|
||||||
|
|
||||||
|
ul
|
||||||
|
list-style-type: none
|
||||||
|
ul, li
|
||||||
|
display: inline-block
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
span.filename
|
||||||
|
padding: 0 0.5em
|
||||||
|
display: block
|
||||||
|
overflow: hidden
|
||||||
|
white-space: nowrap
|
||||||
|
text-overflow: ellipsis
|
||||||
|
|
||||||
|
.body
|
||||||
|
margin: 1em
|
||||||
|
|
||||||
|
.anonymous
|
||||||
|
margin: 0.3em 0
|
||||||
|
|
||||||
|
.safety
|
||||||
|
margin: 0.3em 0
|
||||||
|
label
|
||||||
|
display: inline-block
|
||||||
|
margin-right: 1em
|
||||||
|
|
||||||
|
.options div
|
||||||
|
display: inline-block
|
||||||
|
margin: 0 1em 0 0
|
||||||
|
|
||||||
|
.messages
|
||||||
|
margin-top: 1em
|
||||||
|
.message:last-child
|
||||||
|
margin-bottom: 0
|
||||||
|
|
||||||
|
.lookalikes
|
||||||
|
list-style-type: none
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
li
|
||||||
|
clear: both
|
||||||
|
margin: 1em 0 0 0
|
||||||
|
padding-left: 7em
|
||||||
|
font-size: 90%
|
||||||
|
|
||||||
|
.thumbnail-wrapper
|
||||||
|
float: left
|
||||||
|
width: 6em
|
||||||
|
height: 4em
|
||||||
|
margin: 0 0 0 -7em
|
||||||
|
.thumbnail
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
.description
|
||||||
|
margin-right: 0.5em
|
||||||
|
display: inline-block
|
||||||
|
.controls
|
||||||
|
float: right
|
||||||
|
display: inline-block
|
||||||
|
|
||||||
|
|
||||||
|
&:first-child .move-up
|
||||||
|
color: $inactive-link-color
|
||||||
|
&:last-child .move-down
|
||||||
|
color: $inactive-link-color
|
||||||
|
.darktheme &:first-child .move-up
|
||||||
|
color: $inactive-link-color-darktheme
|
||||||
|
.darktheme &:last-child .move-down
|
||||||
|
color: $inactive-link-color-darktheme
|
||||||
|
|
||||||
|
.darktheme #post-upload .uploadables-container .uploadable-container
|
||||||
|
.uploadable header
|
||||||
|
background: $upload-header-background-color-darktheme
|
||||||
|
&:first-child .move-up
|
||||||
|
color: $inactive-link-color-darktheme
|
||||||
|
&:last-child .move-down
|
||||||
|
color: $inactive-link-color-darktheme
|
64
client/css/snapshots-list-view.styl
Normal file
64
client/css/snapshots-list-view.styl
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
$snapshot-created-background-color = #E0F5E0
|
||||||
|
$snapshot-modified-background-color = #E0F5FF
|
||||||
|
$snapshot-deleted-background-color = #FDE5E5
|
||||||
|
$snapshot-merged-background-color = #FEC
|
||||||
|
|
||||||
|
.snapshot-list
|
||||||
|
text-align: left
|
||||||
|
|
||||||
|
ul
|
||||||
|
margin: 0 auto
|
||||||
|
padding: 0
|
||||||
|
width: 100%
|
||||||
|
max-width: 35em
|
||||||
|
list-style-type: none
|
||||||
|
|
||||||
|
li
|
||||||
|
margin-bottom: 1em
|
||||||
|
&:last-child
|
||||||
|
margin-bottom: 0
|
||||||
|
|
||||||
|
.time
|
||||||
|
float: right
|
||||||
|
|
||||||
|
div
|
||||||
|
padding: 0.1em 0.5em
|
||||||
|
.thumbnail
|
||||||
|
margin: 0 0.4em 0 0
|
||||||
|
&:empty
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
div.operation-created
|
||||||
|
background: $snapshot-created-background-color
|
||||||
|
&+.details
|
||||||
|
background: alpha(@background, 50%)
|
||||||
|
div.operation-modified
|
||||||
|
background: $snapshot-modified-background-color
|
||||||
|
&+.details
|
||||||
|
background: alpha(@background, 50%)
|
||||||
|
div.operation-deleted
|
||||||
|
background: $snapshot-deleted-background-color
|
||||||
|
&+.details
|
||||||
|
background: alpha(@background, 50%)
|
||||||
|
div.operation-merged
|
||||||
|
background: $snapshot-merged-background-color
|
||||||
|
&+.details
|
||||||
|
background: alpha(@background, 50%)
|
||||||
|
|
||||||
|
.darktheme .snapshot-list ul li
|
||||||
|
div.operation-created
|
||||||
|
background: darken($snapshot-created-background-color, 80%)
|
||||||
|
&+.details
|
||||||
|
background: alpha(@background, 50%)
|
||||||
|
div.operation-modified
|
||||||
|
background: darken($snapshot-modified-background-color, 80%)
|
||||||
|
&+.details
|
||||||
|
background: alpha(@background, 50%)
|
||||||
|
div.operation-deleted
|
||||||
|
background: darken($snapshot-deleted-background-color, 80%)
|
||||||
|
&+.details
|
||||||
|
background: alpha(@background, 50%)
|
||||||
|
div.operation-merged
|
||||||
|
background: darken($snapshot-merged-background-color, 80%)
|
||||||
|
&+.details
|
||||||
|
background: alpha(@background, 50%)
|
29
client/css/tag-categories-view.styl
Normal file
29
client/css/tag-categories-view.styl
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
.content-wrapper.tag-categories
|
||||||
|
width: 100%
|
||||||
|
max-width: 45em
|
||||||
|
table
|
||||||
|
border-spacing: 0
|
||||||
|
width: 100%
|
||||||
|
tr.default td
|
||||||
|
background: $default-tag-category-background-color
|
||||||
|
td, th
|
||||||
|
padding: .4em
|
||||||
|
&.color
|
||||||
|
input[type=text]
|
||||||
|
width: 8em
|
||||||
|
&.usages
|
||||||
|
text-align: center
|
||||||
|
&.remove, &.set-default
|
||||||
|
white-space: pre
|
||||||
|
th
|
||||||
|
white-space: nowrap
|
||||||
|
&:first-child
|
||||||
|
padding-left: 0
|
||||||
|
&:last-child
|
||||||
|
padding-right: 0
|
||||||
|
tfoot
|
||||||
|
display: none
|
||||||
|
form
|
||||||
|
width: auto
|
161
client/css/tag-input-control.styl
Normal file
161
client/css/tag-input-control.styl
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
div.tag-input
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
.main-control
|
||||||
|
display: flex
|
||||||
|
input
|
||||||
|
flex: 5
|
||||||
|
button
|
||||||
|
flex: 1
|
||||||
|
margin: 0 0 0 0.5em
|
||||||
|
|
||||||
|
|
||||||
|
.tag-suggestions
|
||||||
|
position: absolute
|
||||||
|
z-index: 5
|
||||||
|
top: 0
|
||||||
|
left: 100%
|
||||||
|
|
||||||
|
&:not(.shown)
|
||||||
|
display: none
|
||||||
|
|
||||||
|
&.translucent
|
||||||
|
opacity: .5
|
||||||
|
|
||||||
|
&:before
|
||||||
|
margin-left: 0.5em
|
||||||
|
margin-top: 0.5em
|
||||||
|
position: absolute
|
||||||
|
display: block
|
||||||
|
background: $tag-suggestions-header-color
|
||||||
|
border-left: 1px solid $tag-suggestions-border-color
|
||||||
|
border-bottom: 1px solid $tag-suggestions-border-color
|
||||||
|
width: 0.707107em
|
||||||
|
height: 0.707107em
|
||||||
|
content: ' '
|
||||||
|
transform: rotate(45deg)
|
||||||
|
transform-origin: 0 0%
|
||||||
|
|
||||||
|
.buttons
|
||||||
|
float: right
|
||||||
|
a
|
||||||
|
margin-left: 1em
|
||||||
|
color: $inactive-link-color
|
||||||
|
|
||||||
|
.wrapper
|
||||||
|
margin-left: 0.5em
|
||||||
|
background: $window-color
|
||||||
|
border: 1px solid $tag-suggestions-border-color
|
||||||
|
width: 15em
|
||||||
|
word-break: break-all
|
||||||
|
p
|
||||||
|
background: $tag-suggestions-header-color
|
||||||
|
padding: 0.2em 1em
|
||||||
|
margin: 0
|
||||||
|
ul
|
||||||
|
list-style-type: none
|
||||||
|
margin: 0
|
||||||
|
overflow-y: auto
|
||||||
|
overflow-x: none
|
||||||
|
max-height: 20em
|
||||||
|
padding: 0.5em 1em 0 1em
|
||||||
|
li:last-child
|
||||||
|
border-bottom: 0.5em solid alpha($window-color, 0)
|
||||||
|
li
|
||||||
|
margin: 0
|
||||||
|
font-size: 90%
|
||||||
|
line-height: 1.3
|
||||||
|
a, span
|
||||||
|
display: inline-block
|
||||||
|
vertical-align: bottom
|
||||||
|
.add-tag
|
||||||
|
white-space: nowrap
|
||||||
|
overflow: hidden
|
||||||
|
max-width: 10em
|
||||||
|
text-overflow: ellipsis
|
||||||
|
.tag-weight
|
||||||
|
margin: 0 1em 0 0
|
||||||
|
p
|
||||||
|
margin: 0
|
||||||
|
|
||||||
|
.append
|
||||||
|
color: $inactive-link-color
|
||||||
|
margin-left: 0.7em
|
||||||
|
font-size: 90%
|
||||||
|
unselectable()
|
||||||
|
|
||||||
|
@keyframes tag-added-to-post
|
||||||
|
from
|
||||||
|
max-height: 0
|
||||||
|
to
|
||||||
|
max-height: 5em
|
||||||
|
|
||||||
|
ul.compact-tags
|
||||||
|
width: 100%
|
||||||
|
margin: 0.5em 0 0 0
|
||||||
|
padding: 0
|
||||||
|
li
|
||||||
|
margin: 0
|
||||||
|
width: 100%
|
||||||
|
line-height: 140%
|
||||||
|
white-space: nowrap
|
||||||
|
overflow: hidden
|
||||||
|
text-overflow: ellipsis
|
||||||
|
transition: background-color 0.5s linear
|
||||||
|
a
|
||||||
|
display: inline
|
||||||
|
a:focus
|
||||||
|
outline: 0
|
||||||
|
box-shadow: inset 0 0 0 2px $main-color
|
||||||
|
// these 3 added when tag is added to ul
|
||||||
|
&.added, &.new, &.implication
|
||||||
|
animation: tag-added-to-post 1s ease forwards
|
||||||
|
&.implication
|
||||||
|
color: $implied-tag-text-color
|
||||||
|
background-color: $implied-tag-background-color
|
||||||
|
&.new
|
||||||
|
color: $new-tag-text-color
|
||||||
|
background-color: $new-tag-background-color
|
||||||
|
&.duplicate
|
||||||
|
color: $duplicate-tag-text-color
|
||||||
|
background-color: $duplicate-tag-background-color
|
||||||
|
i
|
||||||
|
padding-right: 0.4em
|
||||||
|
|
||||||
|
.darktheme ul.compact-tags
|
||||||
|
li
|
||||||
|
&.new
|
||||||
|
background-color: darken($new-tag-background-color, 80%)
|
||||||
|
&.implication
|
||||||
|
background-color: darken($implied-tag-background-color, 85%)
|
||||||
|
&.duplicate
|
||||||
|
background-color: darken($duplicate-tag-background-color, 80%)
|
||||||
|
|
||||||
|
div.tag-input, ul.compact-tags
|
||||||
|
.tag-usages, .tag-weight, .remove-tag
|
||||||
|
color: $inactive-link-color
|
||||||
|
unselectable()
|
||||||
|
.tag-usages, .tag-weight
|
||||||
|
font-size: 90%
|
||||||
|
.tag-usages, .tag-weight
|
||||||
|
margin-left: 0.7em
|
||||||
|
.remove-tag
|
||||||
|
margin-right: 0.5em
|
||||||
|
|
||||||
|
.darktheme
|
||||||
|
div.tag-input .tag-suggestions
|
||||||
|
.buttons a
|
||||||
|
color: $inactive-link-color-darktheme
|
||||||
|
.wrapper
|
||||||
|
background: $window-color-darktheme
|
||||||
|
ul li:last-child
|
||||||
|
border-bottom: 0.5em solid alpha($window-color-darktheme, 0)
|
||||||
|
p
|
||||||
|
background: darken($tag-suggestions-header-color, 80%)
|
||||||
|
.append
|
||||||
|
color: $inactive-link-color-darktheme
|
||||||
|
div.tag-input, ul.compact-tags
|
||||||
|
.tag-usages, .tag-weight, .remove-tag
|
||||||
|
color: $inactive-link-color-darktheme
|
67
client/css/tag-list-view.styl
Normal file
67
client/css/tag-list-view.styl
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
.tag-list
|
||||||
|
table
|
||||||
|
width: 100%
|
||||||
|
border-spacing: 0
|
||||||
|
text-align: left
|
||||||
|
line-height: 1.3em
|
||||||
|
tr:hover td
|
||||||
|
background: $top-navigation-color
|
||||||
|
th, td
|
||||||
|
padding: 0.1em 0.5em
|
||||||
|
th
|
||||||
|
white-space: nowrap
|
||||||
|
background: $top-navigation-color
|
||||||
|
.names
|
||||||
|
width: 28%
|
||||||
|
.implications
|
||||||
|
width: 28%
|
||||||
|
.suggestions
|
||||||
|
width: 28%
|
||||||
|
.usages
|
||||||
|
text-align: center
|
||||||
|
width: 8%
|
||||||
|
.creation-time
|
||||||
|
text-align: center
|
||||||
|
width: 8%
|
||||||
|
white-space: pre
|
||||||
|
ul
|
||||||
|
list-style-type: none
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
display: inline
|
||||||
|
li
|
||||||
|
padding: 0
|
||||||
|
display: inline
|
||||||
|
&:not(:last-child):after
|
||||||
|
content: ', '
|
||||||
|
@media (max-width: 800px)
|
||||||
|
.implications, .suggestions
|
||||||
|
display: none
|
||||||
|
|
||||||
|
.darktheme .tag-list
|
||||||
|
table
|
||||||
|
tr:hover td
|
||||||
|
background: $top-navigation-color-darktheme
|
||||||
|
th
|
||||||
|
background: $top-navigation-color-darktheme
|
||||||
|
|
||||||
|
.tag-list-header
|
||||||
|
label
|
||||||
|
display: none !important
|
||||||
|
text-align: left
|
||||||
|
form
|
||||||
|
width: auto
|
||||||
|
input[name=search-text]
|
||||||
|
width: 25em
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
width: 100%
|
||||||
|
.append
|
||||||
|
vertical-align: middle
|
||||||
|
font-size: 0.95em
|
||||||
|
color: $inactive-link-color
|
||||||
|
|
||||||
|
.darktheme .tag-list-header
|
||||||
|
.append
|
||||||
|
color: $inactive-link-color-darktheme
|
33
client/css/tag-view.styl
Normal file
33
client/css/tag-view.styl
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#tag
|
||||||
|
width: 100%
|
||||||
|
max-width: 40em
|
||||||
|
h1
|
||||||
|
word-break: break-all
|
||||||
|
line-height: 130%
|
||||||
|
margin-top: 0
|
||||||
|
form
|
||||||
|
width: 100%
|
||||||
|
.tag-edit
|
||||||
|
textarea
|
||||||
|
height: 10em
|
||||||
|
.tag-summary
|
||||||
|
section
|
||||||
|
&.description
|
||||||
|
margin: 1.5em 0 0 0
|
||||||
|
&.details
|
||||||
|
vertical-align: top
|
||||||
|
padding-right: 0.5em
|
||||||
|
ul
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
list-style-type: none
|
||||||
|
li
|
||||||
|
display: inline
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
li:not(:last-of-type):after
|
||||||
|
content: ', '
|
||||||
|
ul:empty:after
|
||||||
|
content: '(none)'
|
||||||
|
section
|
||||||
|
margin-bottom: 1em
|
51
client/css/user-list-view.styl
Normal file
51
client/css/user-list-view.styl
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
.user-list
|
||||||
|
ul
|
||||||
|
list-style-type: none
|
||||||
|
padding: 0
|
||||||
|
display: flex
|
||||||
|
align-content: flex-end
|
||||||
|
flex-wrap: wrap
|
||||||
|
margin: 0 -0.5em
|
||||||
|
li
|
||||||
|
flex-grow: 1
|
||||||
|
width: 20em
|
||||||
|
margin: 0 0.5em 1em 0.5em
|
||||||
|
padding: 0.75em
|
||||||
|
vertical-align: top
|
||||||
|
background: $top-navigation-color
|
||||||
|
text-align: left
|
||||||
|
.wrapper
|
||||||
|
display: flex
|
||||||
|
.details
|
||||||
|
font-size: 90%
|
||||||
|
line-height: 130%
|
||||||
|
.image
|
||||||
|
margin: 0.25em 0.6em 0.25em 0
|
||||||
|
.thumbnail
|
||||||
|
width: 3em
|
||||||
|
height: 3em
|
||||||
|
|
||||||
|
.darktheme .user-list
|
||||||
|
ul li
|
||||||
|
background: $top-navigation-color-darktheme
|
||||||
|
|
||||||
|
.user-list-header
|
||||||
|
label
|
||||||
|
display: none !important
|
||||||
|
text-align: left
|
||||||
|
form
|
||||||
|
width: auto
|
||||||
|
input[name=search-text]
|
||||||
|
width: 25em
|
||||||
|
@media (max-width: 1000px)
|
||||||
|
width: 100%
|
||||||
|
.append
|
||||||
|
vertical-align: middle
|
||||||
|
font-size: 0.95em
|
||||||
|
color: $inactive-link-color
|
||||||
|
|
||||||
|
.darktheme .user-list-header
|
||||||
|
.append
|
||||||
|
color: $inactive-link-color-darktheme
|
29
client/css/user-registration.styl
Normal file
29
client/css/user-registration.styl
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
@import colors
|
||||||
|
|
||||||
|
#user-registration
|
||||||
|
padding-bottom: calc(2vw - 1em) !important
|
||||||
|
form
|
||||||
|
float: left
|
||||||
|
margin-right: 3em
|
||||||
|
margin-bottom: 1em
|
||||||
|
.info
|
||||||
|
float: left
|
||||||
|
border-radius: 0.2em
|
||||||
|
width: 20em
|
||||||
|
margin-bottom: 1em
|
||||||
|
ul
|
||||||
|
line-height: 1.8em
|
||||||
|
list-style-type: none
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
li
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
i
|
||||||
|
margin-right: 0.5em
|
||||||
|
i.fa
|
||||||
|
color: $main-color
|
||||||
|
p:first-child
|
||||||
|
margin: 0 0 0.5em 0
|
||||||
|
p:last-child
|
||||||
|
margin-bottom: 0
|
82
client/css/user-view.styl
Normal file
82
client/css/user-view.styl
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
@import colors
|
||||||
|
$token-border-color = $active-tab-background-color
|
||||||
|
|
||||||
|
#user
|
||||||
|
width: 100%
|
||||||
|
max-width: 35em
|
||||||
|
nav.text-nav
|
||||||
|
margin-bottom: 1.5em
|
||||||
|
|
||||||
|
#user-summary
|
||||||
|
.thumbnail
|
||||||
|
width: 6em
|
||||||
|
height: 6em
|
||||||
|
margin: 0 1.5em 1.5em 0
|
||||||
|
float: left
|
||||||
|
.basic-info
|
||||||
|
list-style-type: none
|
||||||
|
margin: 0
|
||||||
|
div
|
||||||
|
clear: both
|
||||||
|
nav
|
||||||
|
float: left
|
||||||
|
width: 45%
|
||||||
|
margin-right: 1em
|
||||||
|
|
||||||
|
#user-edit
|
||||||
|
form
|
||||||
|
width: 100%
|
||||||
|
.avatar
|
||||||
|
#avatar-content
|
||||||
|
float: right
|
||||||
|
width: 65%
|
||||||
|
margin-top: .5em
|
||||||
|
#avatar-radio
|
||||||
|
float: left
|
||||||
|
width: 30%
|
||||||
|
&:after
|
||||||
|
content: ' '
|
||||||
|
display: block
|
||||||
|
height: 1px
|
||||||
|
clear: both
|
||||||
|
|
||||||
|
#user-tokens
|
||||||
|
|
||||||
|
.token-flex-container
|
||||||
|
width: 100%
|
||||||
|
display: flex;
|
||||||
|
flex-direction column;
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
|
||||||
|
.full-width
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.token-flex-row
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0.2em;
|
||||||
|
|
||||||
|
.no-wrap
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
.token-input
|
||||||
|
min-height: 2em;
|
||||||
|
line-height: 2em;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.token-flex-column
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.token-flex-labels
|
||||||
|
padding-right: 0.5em
|
||||||
|
|
||||||
|
hr
|
||||||
|
border-top: 3px solid $token-border-color
|
||||||
|
|
||||||
|
form
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
#user-delete form
|
||||||
|
width: 100%
|
11
client/docker-start.sh
Executable file
11
client/docker-start.sh
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/dumb-init /bin/sh
|
||||||
|
|
||||||
|
# Integrate environment variables
|
||||||
|
sed -i "s|__BACKEND__|${BACKEND_HOST}|" \
|
||||||
|
/etc/nginx/nginx.conf
|
||||||
|
sed -i "s|__BASEURL__|${BASE_URL:-/}|g" \
|
||||||
|
/var/www/index.htm \
|
||||||
|
/var/www/manifest.json
|
||||||
|
|
||||||
|
# Start server
|
||||||
|
exec nginx
|
BIN
client/fonts/open_sans.woff2
Normal file
BIN
client/fonts/open_sans.woff2
Normal file
Binary file not shown.
85
client/html/comment.tpl
Normal file
85
client/html/comment.tpl
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<div class='comment-container'>
|
||||||
|
<div class='avatar'>
|
||||||
|
<% if (ctx.user && ctx.user.name && ctx.canViewUsers) { %>
|
||||||
|
<a href='<%- ctx.formatClientLink('user', ctx.user.name) %>'>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<%= ctx.makeThumbnail(ctx.user ? ctx.user.avatarUrl : null) %>
|
||||||
|
|
||||||
|
<% if (ctx.user && ctx.user.name && ctx.canViewUsers) { %>
|
||||||
|
</a>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='comment'>
|
||||||
|
<header>
|
||||||
|
<nav class='edit tabs'>
|
||||||
|
<ul>
|
||||||
|
<li class='edit'><a href>Write</a></li>
|
||||||
|
<li class='preview'><a href>Preview</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<nav class='readonly'><%
|
||||||
|
%><strong><span class='nickname'><%
|
||||||
|
%><% if (ctx.user && ctx.user.name && ctx.canViewUsers) { %><%
|
||||||
|
%><a href='<%- ctx.formatClientLink('user', ctx.user.name) %>'><%
|
||||||
|
%><% } %><%
|
||||||
|
|
||||||
|
%><%- ctx.user ? ctx.user.name : 'Deleted user' %><%
|
||||||
|
|
||||||
|
%><% if (ctx.user && ctx.user.name && ctx.canViewUsers) { %><%
|
||||||
|
%></a><%
|
||||||
|
%><% } %><%
|
||||||
|
%></span></strong>
|
||||||
|
|
||||||
|
<span class='date'><%
|
||||||
|
%>commented <%= ctx.makeRelativeTime(ctx.comment ? ctx.comment.creationTime : null) %><%
|
||||||
|
%></span><%
|
||||||
|
|
||||||
|
%><wbr><%
|
||||||
|
|
||||||
|
%><span class='score-container'></span><%
|
||||||
|
|
||||||
|
%><% if (ctx.canEditComment || ctx.canDeleteComment) { %><%
|
||||||
|
%><span class='action-container'><%
|
||||||
|
%><% if (ctx.canEditComment) { %><%
|
||||||
|
%><a href class='edit'><%
|
||||||
|
%><i class='fa fa-pencil'></i> edit<%
|
||||||
|
%></a><%
|
||||||
|
%><% } %><%
|
||||||
|
|
||||||
|
%><% if (ctx.canDeleteComment) { %><%
|
||||||
|
%><a href class='delete'><%
|
||||||
|
%><i class='fa fa-remove'></i> delete<%
|
||||||
|
%></a><%
|
||||||
|
%><% } %><%
|
||||||
|
%></span><%
|
||||||
|
%><% } %><%
|
||||||
|
%></nav><%
|
||||||
|
%></header>
|
||||||
|
|
||||||
|
<form class='body'>
|
||||||
|
<div class='keep-height'>
|
||||||
|
<div class='tab preview'>
|
||||||
|
<div class='comment-content'>
|
||||||
|
<%= ctx.makeMarkdown(ctx.comment ? ctx.comment.text : '') %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='tab edit'>
|
||||||
|
<textarea required minlength=1><%- ctx.comment ? ctx.comment.text : '' %></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav class='edit'>
|
||||||
|
<div class='messages'></div>
|
||||||
|
|
||||||
|
<input type='submit' class='save-changes' value='Save'/>
|
||||||
|
<% if (!ctx.onlyEditing) { %>
|
||||||
|
<input type='button' class='cancel-editing discourage' value='Cancel'/>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
4
client/html/comment_list.tpl
Normal file
4
client/html/comment_list.tpl
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<div class='comments'>
|
||||||
|
<ul>
|
||||||
|
</ul>
|
||||||
|
</div>
|
18
client/html/comments_page.tpl
Normal file
18
client/html/comments_page.tpl
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<div class='global-comment-list'>
|
||||||
|
<ul><!--
|
||||||
|
--><% for (let post of ctx.response.results) { %><!--
|
||||||
|
--><li><!--
|
||||||
|
--><div class='post-thumbnail'><!--
|
||||||
|
--><% if (ctx.canViewPosts) { %><!--
|
||||||
|
--><a href='<%- ctx.formatClientLink('post', post.id) %>'><!--
|
||||||
|
--><% } %><!--
|
||||||
|
--><%= ctx.makeThumbnail(post.thumbnailUrl) %><!--
|
||||||
|
--><% if (ctx.canViewPosts) { %><!--
|
||||||
|
--></a><!--
|
||||||
|
--><% } %><!--
|
||||||
|
--></div><!--
|
||||||
|
--><div class='comments-container' data-for='<%- post.id %>'></div><!--
|
||||||
|
--></li><!--
|
||||||
|
--><% } %><!--
|
||||||
|
--></ul>
|
||||||
|
</div>
|
7
client/html/endless_pager.tpl
Normal file
7
client/html/endless_pager.tpl
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<div class='pager'>
|
||||||
|
<div class='page-header-holder'></div>
|
||||||
|
<div class='messages'></div>
|
||||||
|
<div class='page-guard top'></div>
|
||||||
|
<div class='pages-holder'></div>
|
||||||
|
<div class='page-guard bottom'></div>
|
||||||
|
</div>
|
4
client/html/endless_pager_page.tpl
Normal file
4
client/html/endless_pager_page.tpl
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<div class='page'>
|
||||||
|
<p class='page-header'><span>Page <%- ctx.page %> of <%- ctx.totalPages %></span></p>
|
||||||
|
<div class='page-content-holder'></div>
|
||||||
|
</div>
|
10
client/html/expander.tpl
Normal file
10
client/html/expander.tpl
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<section class='expander'>
|
||||||
|
<header>
|
||||||
|
<a href>
|
||||||
|
<span><%- ctx.title %></span>
|
||||||
|
<i class='fa fa-chevron-down'></i>
|
||||||
|
</a>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class='expander-content'></div>
|
||||||
|
</section>
|
15
client/html/fav.tpl
Normal file
15
client/html/fav.tpl
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<% if (ctx.canFavorite) { %>
|
||||||
|
<% if (ctx.ownFavorite) { %>
|
||||||
|
<a href class='remove-favorite'>
|
||||||
|
<i class='fa fa-heart'></i>
|
||||||
|
<% } else { %>
|
||||||
|
<a href class='add-favorite'>
|
||||||
|
<i class='fa fa-heart-o'></i>
|
||||||
|
<% } %>
|
||||||
|
<% } else { %>
|
||||||
|
<a class='add-favorite inactive'>
|
||||||
|
<i class='fa fa-heart-o'></i>
|
||||||
|
<% } %>
|
||||||
|
<span class='vim-nav-hint'>add to favorites</span>
|
||||||
|
</a>
|
||||||
|
<span class='value'><%- ctx.favoriteCount %></span>
|
26
client/html/file_dropper.tpl
Normal file
26
client/html/file_dropper.tpl
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<div class='file-dropper-holder'>
|
||||||
|
<input type='file' id='<%- ctx.id %>'/>
|
||||||
|
<label class='file-dropper' for='<%- ctx.id %>' role='button'>
|
||||||
|
<% if (ctx.allowMultiple) { %>
|
||||||
|
Drop files here!
|
||||||
|
<% } else { %>
|
||||||
|
Drop file here!
|
||||||
|
<% } %>
|
||||||
|
<br/>
|
||||||
|
Or just click on this box.
|
||||||
|
<% if (ctx.extraText) { %>
|
||||||
|
<br/>
|
||||||
|
<small><%= ctx.extraText %></small>
|
||||||
|
<% } %>
|
||||||
|
</label>
|
||||||
|
<% if (ctx.allowUrls) { %>
|
||||||
|
<div class='url-holder'>
|
||||||
|
<input type='text' name='url' placeholder='<%- ctx.urlPlaceholder %>'/>
|
||||||
|
<% if (ctx.lock) { %>
|
||||||
|
<button>Confirm</button>
|
||||||
|
<% } else { %>
|
||||||
|
<button>Add URL</button>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
13
client/html/help.tpl
Normal file
13
client/html/help.tpl
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<div class='content-wrapper' id='help'>
|
||||||
|
<nav class='buttons primary'><!--
|
||||||
|
--><ul><!--
|
||||||
|
--><li data-name='about'><a href='<%- ctx.formatClientLink('help', 'about') %>'>About</a></li><!--
|
||||||
|
--><li data-name='keyboard'><a href='<%- ctx.formatClientLink('help', 'keyboard') %>'>Keyboard</a></li><!--
|
||||||
|
--><li data-name='search'><a href='<%- ctx.formatClientLink('help', 'search') %>'>Search syntax</a></li><!--
|
||||||
|
--><li data-name='comments'><a href='<%- ctx.formatClientLink('help', 'comments') %>'>Comments</a></li><!--
|
||||||
|
--><li data-name='tos'><a href='<%- ctx.formatClientLink('help', 'tos') %>'>Terms of service</a></li><!--
|
||||||
|
--></ul><!--
|
||||||
|
--></nav>
|
||||||
|
|
||||||
|
<div class='content'></div>
|
||||||
|
</div>
|
13
client/html/help_about.tpl
Normal file
13
client/html/help_about.tpl
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<p>Szurubooru is an image board engine inspired by services such as Danbooru,
|
||||||
|
Gelbooru and Moebooru. Its name <a href='http://sjp.pwn.pl/sjp/;2527372'>has
|
||||||
|
its roots in Polish language and has onomatopeic meaning of scraping or
|
||||||
|
scrubbing</a>. It is pronounced as <em>shoorubooru</em>.</p>
|
||||||
|
|
||||||
|
<p class='section'><strong>Registration</strong></p>
|
||||||
|
|
||||||
|
<p>The e-mail you enter during account creation is only used to retrieve your
|
||||||
|
Gravatar and for password reminders. Only you can see it (well, except the
|
||||||
|
database staff… we won’t spam your mailbox anyway).</p>
|
||||||
|
|
||||||
|
<p>Oh, and you can delete your account at any time. Posts you uploaded will
|
||||||
|
stay, unless some angry admin removes them.</p>
|
38
client/html/help_comments.tpl
Normal file
38
client/html/help_comments.tpl
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<p>Comments support Markdown syntax, extended by some handy tags:</p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>@426</code></td>
|
||||||
|
<td>links to post number 426</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>#Dragon_Ball</code></td>
|
||||||
|
<td>links to tag “Dragon_Ball”</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>+Pirate</code></td>
|
||||||
|
<td>links to user “Pirate”</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>~~new~~</code></td>
|
||||||
|
<td>adds strike-through</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>[spoiler]Lelouch survives[/spoiler]</td>
|
||||||
|
<td>marks text as spoiler and hides it</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>[sjis](´・ω・`)[/sjis]</td>
|
||||||
|
<td>adds SJIS art</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>You can also specify the size of embedded images like this:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><code>![alt](href =WIDTHx "title")</code></li>
|
||||||
|
<li><code>![alt](href =xHEIGHT "title")</code></li>
|
||||||
|
<li><code>![alt](href =WIDTHxHEIGHT "title")</code></li>
|
||||||
|
</ul>
|
47
client/html/help_keyboard.tpl
Normal file
47
client/html/help_keyboard.tpl
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<p>You can use your keyboard to navigate around the site. There are a few
|
||||||
|
shortcuts:</p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Hotkey</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><kbd>Q</kbd></td>
|
||||||
|
<td>Focus search field, if available</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><kbd>A</kbd> and <kbd>D</kbd>, <kbd>←</kbd> and <kbd>→</kbd></td>
|
||||||
|
<td>Go to newer/older page or post</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><kbd>F</kbd></td>
|
||||||
|
<td>Cycle post fit mode</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><kbd>E</kbd></td>
|
||||||
|
<td>Edit post</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><kbd>P</kbd></td>
|
||||||
|
<td>Focus first post in post list</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><kbd>Delete</kbd></td>
|
||||||
|
<td>Delete post (while in edit mode)</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>Additionally, each item in the top navigation can be accessed using a
|
||||||
|
feature called “access keys”. Pressing the underlined letter while
|
||||||
|
holding Shift or Alt+Shift (depending on your browser) will go to the desired
|
||||||
|
page (most browsers) or focus the link (IE).</p>
|
11
client/html/help_search.tpl
Normal file
11
client/html/help_search.tpl
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<nav class='buttons secondary'><!--
|
||||||
|
--><ul><!--
|
||||||
|
--><li data-name='default'><a href='<%- ctx.formatClientLink('help', 'search') %>'>General</a></li><!--
|
||||||
|
--><li data-name='posts'><a href='<%- ctx.formatClientLink('help', 'search', 'posts') %>'>Posts</a></li><!--
|
||||||
|
--><li data-name='users'><a href='<%- ctx.formatClientLink('help', 'search', 'users') %>'>Users</a></li><!--
|
||||||
|
--><li data-name='tags'><a href='<%- ctx.formatClientLink('help', 'search', 'tags') %>'>Tags</a></li><!--
|
||||||
|
--><li data-name='pools'><a href='<%- ctx.formatClientLink('help', 'search', 'pools') %>'>Pools</li><!--
|
||||||
|
--></ul><!--
|
||||||
|
--></nav>
|
||||||
|
|
||||||
|
<div class='subcontent'></div>
|
99
client/html/help_search_general.tpl
Normal file
99
client/html/help_search_general.tpl
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
<p>Search queries are built of tokens that are separated by spaces. Each token
|
||||||
|
can be of following form:</p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Syntax</th>
|
||||||
|
<th>Token type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code><value></code></td>
|
||||||
|
<td>anonymous tokens</td>
|
||||||
|
<td>used for basic filters</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code><key>:<value></code></td>
|
||||||
|
<td>named tokens</td>
|
||||||
|
<td>used for advanced filters</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>sort:<style></code></td>
|
||||||
|
<td>sort style tokens</td>
|
||||||
|
<td>used to sort the results</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>special:<value></code></td>
|
||||||
|
<td>special tokens</td>
|
||||||
|
<td>filters usually tied to the logged in user</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>Most of anonymous and named tokens support ranged and composite values that
|
||||||
|
take following form:</p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>a,b,c</code></td>
|
||||||
|
<td>will show things that satisfy either <code>a</code>,
|
||||||
|
<code>b</code> or <code>c</code>.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>1..</code></td>
|
||||||
|
<td>will show things that are equal to or greater than 1.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>..4</code></td>
|
||||||
|
<td>will show things that are equal to at most 4.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>1..4</code></td>
|
||||||
|
<td>will show things that are equal to 1, 2, 3 or 4.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>Ranged values can be also supplied by appending <code>-min</code> or
|
||||||
|
<code>-max</code> to the key, for example like this:
|
||||||
|
<code>score-min:1</code>.</p>
|
||||||
|
|
||||||
|
<p>Date/time values can be of following form:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><code>today</code></li>
|
||||||
|
<li><code>yesterday</code></li>
|
||||||
|
<li><code><year></code></li>
|
||||||
|
<li><code><year>-<month></code></li>
|
||||||
|
<li><code><year>-<month>-<day></code></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>Some fields, such as user names, can take wildcards (<code>*</code>).</p>
|
||||||
|
|
||||||
|
<p>All tokens can be negated by prepending them with <code>-</code>.</p>
|
||||||
|
|
||||||
|
<p>Sort style token values can be appended with <code>,asc</code> or
|
||||||
|
<code>,desc</code> to control the sort direction, which can be also controlled
|
||||||
|
by negating the whole token.</p>
|
||||||
|
|
||||||
|
<p>You can escape special characters such as <code>:</code> and <code>-</code>
|
||||||
|
by prepending them with a backslash: <code>\\</code>.</p>
|
||||||
|
|
||||||
|
<h1>Example</h1>
|
||||||
|
|
||||||
|
<p>Searching for posts with following query:</p>
|
||||||
|
|
||||||
|
<pre><code>sea -fav-count:8.. type:swf uploader:Pirate</code></pre>
|
||||||
|
|
||||||
|
<p>will show flash files tagged as sea, that were liked by seven people at
|
||||||
|
most, uploaded by user Pirate.</p>
|
||||||
|
|
||||||
|
<p>Searching for posts with <code>re:zero</code> will show an error message
|
||||||
|
about unknown named token.</p>
|
||||||
|
|
||||||
|
<p>Searching for posts with <code>re\:zero</code> will show posts tagged with
|
||||||
|
<code>re:zero</code>.</p>
|
97
client/html/help_search_pools.tpl
Normal file
97
client/html/help_search_pools.tpl
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
<p><strong>Anonymous tokens</strong></p>
|
||||||
|
|
||||||
|
<p>Same as <code>name</code> token.</p>
|
||||||
|
|
||||||
|
<p><strong>Named tokens</strong></p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>name</code></td>
|
||||||
|
<td>having given name (accepts wildcards)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>category</code></td>
|
||||||
|
<td>having given category (accepts wildcards)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-date</code></td>
|
||||||
|
<td>created at given date</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-time</code></td>
|
||||||
|
<td>alias of <code>creation-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-edit-date</code></td>
|
||||||
|
<td>edited at given date</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-edit-time</code></td>
|
||||||
|
<td>alias of <code>last-edit-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>edit-date</code></td>
|
||||||
|
<td>alias of <code>last-edit-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>edit-time</code></td>
|
||||||
|
<td>alias of <code>last-edit-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>post-count</code></td>
|
||||||
|
<td>alias of <code>usages</code></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p><strong>Sort style tokens</strong></p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>random</code></td>
|
||||||
|
<td>as random as it can get</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>name</code></td>
|
||||||
|
<td>A to Z</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>category</code></td>
|
||||||
|
<td>category (A to Z)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-date</code></td>
|
||||||
|
<td>recently created first</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-time</code></td>
|
||||||
|
<td>alias of <code>creation-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-edit-date</code></td>
|
||||||
|
<td>recently edited first</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-edit-time</code></td>
|
||||||
|
<td>alias of <code>creation-time</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>edit-date</code></td>
|
||||||
|
<td>alias of <code>creation-time</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>edit-time</code></td>
|
||||||
|
<td>alias of <code>creation-time</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>post-count</code></td>
|
||||||
|
<td>number of posts</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p><strong>Special tokens</strong></p>
|
||||||
|
|
||||||
|
<p>None.</p>
|
356
client/html/help_search_posts.tpl
Normal file
356
client/html/help_search_posts.tpl
Normal file
|
@ -0,0 +1,356 @@
|
||||||
|
<p><strong>Anonymous tokens</strong></p>
|
||||||
|
|
||||||
|
<p>Same as <code>tag</code> token.</p>
|
||||||
|
|
||||||
|
<p><strong>Named tokens</strong></p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>id</code></td>
|
||||||
|
<td>having given post number</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>tag</code></td>
|
||||||
|
<td>having given tag (accepts wildcards)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>score</code></td>
|
||||||
|
<td>having given score</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>uploader</code></td>
|
||||||
|
<td>uploaded by given user (accepts wildcards)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>upload</code></td>
|
||||||
|
<td>alias of <code>uploader</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>submit</code></td>
|
||||||
|
<td>alias of <code>uploader</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>comment</code></td>
|
||||||
|
<td>commented by given user (accepts wildcards)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>fav</code></td>
|
||||||
|
<td>favorited by given user (accepts wildcards)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>source</code></td>
|
||||||
|
<td>having given source URL (accepts wildcards)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>pool</code></td>
|
||||||
|
<td>belonging to the pool with the given ID</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>tag-count</code></td>
|
||||||
|
<td>having given number of tags</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>comment-count</code></td>
|
||||||
|
<td>having given number of comments</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>fav-count</code></td>
|
||||||
|
<td>favorited by given number of users</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>note-count</code></td>
|
||||||
|
<td>having given number of annotations</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>note-text</code></td>
|
||||||
|
<td>having given note text (accepts wildcards)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>relation-count</code></td>
|
||||||
|
<td>having given number of relations</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>feature-count</code></td>
|
||||||
|
<td>having been featured given number of times</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>type</code></td>
|
||||||
|
<td>given type of posts. <code><value></code> can be either <code>image</code>, <code>animation</code> (or <code>animated</code> or <code>anim</code>), <code>flash</code> (or <code>swf</code>) or <code>video</code> (or <code>webm</code>).</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>flag</code></td>
|
||||||
|
<td>having given flag. <code><value></code> can be either <code>loop</code> or <code>sound</code>.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>sha1</code></td>
|
||||||
|
<td>having given SHA1 checksum</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>md5</code></td>
|
||||||
|
<td>having given MD5 checksum</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>content-checksum</code></td>
|
||||||
|
<td>alias of <code>sha1</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>file-size</code></td>
|
||||||
|
<td>having given file size (in bytes)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>image-width</code></td>
|
||||||
|
<td>having given image width (where applicable)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>image-height</code></td>
|
||||||
|
<td>having given image height (where applicable)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>image-area</code></td>
|
||||||
|
<td>having given number of pixels (image width * image height)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>image-aspect-ratio</code></td>
|
||||||
|
<td>having given aspect ratio (image width / image height)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>image-ar</code></td>
|
||||||
|
<td>alias of <code>image-aspect-ratio</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>width</code></td>
|
||||||
|
<td>alias of <code>image-width</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>height</code></td>
|
||||||
|
<td>alias of <code>image-height</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>area</code></td>
|
||||||
|
<td>alias of <code>image-area</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>aspect-ratio</code></td>
|
||||||
|
<td>alias of <code>image-aspect-ratio</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>ar</code></td>
|
||||||
|
<td>alias of <code>image-aspect-ratio</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-date</code></td>
|
||||||
|
<td>posted at given date</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-time</code></td>
|
||||||
|
<td>alias of <code>creation-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>date</code></td>
|
||||||
|
<td>alias of <code>creation-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>time</code></td>
|
||||||
|
<td>alias of <code>creation-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-edit-date</code></td>
|
||||||
|
<td>edited at given date</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-edit-time</code></td>
|
||||||
|
<td>alias of <code>last-edit-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>edit-date</code></td>
|
||||||
|
<td>alias of <code>last-edit-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>edit-time</code></td>
|
||||||
|
<td>alias of <code>last-edit-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>comment-date</code></td>
|
||||||
|
<td>commented at given date</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>comment-time</code></td>
|
||||||
|
<td>alias of <code>comment-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>fav-date</code></td>
|
||||||
|
<td>last favorited at given date</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>fav-time</code></td>
|
||||||
|
<td>alias of <code>fav-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>feature-date</code></td>
|
||||||
|
<td>featured at given date</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>feature-time</code></td>
|
||||||
|
<td>alias of <code>feature-time</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>safety</code></td>
|
||||||
|
<td>having given safety</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>rating</code></td>
|
||||||
|
<td>alias of <code>safety</code></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p><strong>Sort style tokens</strong></p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>random</code></td>
|
||||||
|
<td>as random as it can get</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>id</code></td>
|
||||||
|
<td>highest to lowest post number</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>score</code></td>
|
||||||
|
<td>highest scored</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>tag-count</code></td>
|
||||||
|
<td>with most tags</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>comment-count</code></td>
|
||||||
|
<td>most commented first</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>fav-count</code></td>
|
||||||
|
<td>loved by most</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>note-count</code></td>
|
||||||
|
<td>with most annotations</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>relation-count</code></td>
|
||||||
|
<td>with most relations</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>feature-count</code></td>
|
||||||
|
<td>most often featured</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>file-size</code></td>
|
||||||
|
<td>largest files first</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>image-width</code></td>
|
||||||
|
<td>widest images first</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>image-height</code></td>
|
||||||
|
<td>tallest images first</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>image-area</code></td>
|
||||||
|
<td>largest images first</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>width</code></td>
|
||||||
|
<td>alias of <code>image-width</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>height</code></td>
|
||||||
|
<td>alias of <code>image-height</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>area</code></td>
|
||||||
|
<td>alias of <code>image-area</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-date</code></td>
|
||||||
|
<td>newest to oldest (pretty much same as <code>id</code>)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-time</code></td>
|
||||||
|
<td>alias of <code>creation-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>date</code></td>
|
||||||
|
<td>alias of <code>creation-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>time</code></td>
|
||||||
|
<td>alias of <code>creation-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-edit-date</code></td>
|
||||||
|
<td>like <code>creation-date</code>, only looks at last edit time</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-edit-time</code></td>
|
||||||
|
<td>alias of <code>last-edit-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>edit-date</code></td>
|
||||||
|
<td>alias of <code>last-edit-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>edit-time</code></td>
|
||||||
|
<td>alias of <code>last-edit-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>comment-date</code></td>
|
||||||
|
<td>recently commented by anyone</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>comment-time</code></td>
|
||||||
|
<td>alias of <code>comment-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>fav-date</code></td>
|
||||||
|
<td>recently added to favorites by anyone</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>fav-time</code></td>
|
||||||
|
<td>alias of <code>fav-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>feature-date</code></td>
|
||||||
|
<td>recently featured</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>feature-time</code></td>
|
||||||
|
<td>alias of <code>feature-time</code></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p><strong>Special tokens</strong></p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>liked</code></td>
|
||||||
|
<td>posts liked by currently logged in user</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>disliked</code></td>
|
||||||
|
<td>posts disliked by currently logged in user</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>fav</code></td>
|
||||||
|
<td>posts added to favorites by currently logged in user</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>tumbleweed</code></td>
|
||||||
|
<td>posts with score of 0, without comments and without favorites</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
129
client/html/help_search_tags.tpl
Normal file
129
client/html/help_search_tags.tpl
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
<p><strong>Anonymous tokens</strong></p>
|
||||||
|
|
||||||
|
<p>Same as <code>name</code> token.</p>
|
||||||
|
|
||||||
|
<p><strong>Named tokens</strong></p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>name</code></td>
|
||||||
|
<td>having given name (accepts wildcards)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>category</code></td>
|
||||||
|
<td>having given category (accepts wildcards)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-date</code></td>
|
||||||
|
<td>created at given date</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-time</code></td>
|
||||||
|
<td>alias of <code>creation-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-edit-date</code></td>
|
||||||
|
<td>edited at given date</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-edit-time</code></td>
|
||||||
|
<td>alias of <code>last-edit-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>edit-date</code></td>
|
||||||
|
<td>alias of <code>last-edit-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>edit-time</code></td>
|
||||||
|
<td>alias of <code>last-edit-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>usages</code></td>
|
||||||
|
<td>used in given number of posts</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>usage-count</code></td>
|
||||||
|
<td>alias of <code>usages</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>post-count</code></td>
|
||||||
|
<td>alias of <code>usages</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>suggestion-count</code></td>
|
||||||
|
<td>with given number of suggestions</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>implication-count</code></td>
|
||||||
|
<td>with given number of implications</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p><strong>Sort style tokens</strong></p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>random</code></td>
|
||||||
|
<td>as random as it can get</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>name</code></td>
|
||||||
|
<td>A to Z</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>category</code></td>
|
||||||
|
<td>category (A to Z)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-date</code></td>
|
||||||
|
<td>recently created first</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-time</code></td>
|
||||||
|
<td>alias of <code>creation-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-edit-date</code></td>
|
||||||
|
<td>recently edited first</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-edit-time</code></td>
|
||||||
|
<td>alias of <code>creation-time</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>edit-date</code></td>
|
||||||
|
<td>alias of <code>creation-time</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>edit-time</code></td>
|
||||||
|
<td>alias of <code>creation-time</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>usages</code></td>
|
||||||
|
<td>used in most posts first</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>usage-count</code></td>
|
||||||
|
<td>alias of <code>usages</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>post-count</code></td>
|
||||||
|
<td>alias of <code>usages</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>suggestion-count</code></td>
|
||||||
|
<td>with most suggestions first</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>implication-count</code></td>
|
||||||
|
<td>with most implications first</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p><strong>Special tokens</strong></p>
|
||||||
|
|
||||||
|
<p>None.</p>
|
81
client/html/help_search_users.tpl
Normal file
81
client/html/help_search_users.tpl
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<p><strong>Anonymous tokens</strong></p>
|
||||||
|
|
||||||
|
<p>Same as <code>name</code> token.</p>
|
||||||
|
|
||||||
|
<p><strong>Named tokens</strong></p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>name</code></td>
|
||||||
|
<td>having given name (accepts wildcards)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-date</code></td>
|
||||||
|
<td>registered at given date</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-time</code></td>
|
||||||
|
<td>alias of <code>creation-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-login-date</code>
|
||||||
|
<td>whose most recent login date matches given date</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-login-time</code>
|
||||||
|
<td>alias of <code>last-login-date</code>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>login-date</code>
|
||||||
|
<td>alias of <code>last-login-date</code>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>login-time</code></td>
|
||||||
|
<td>alias of <code>last-login-date</code>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p><strong>Sort style tokens</strong></p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>random</code></td>
|
||||||
|
<td>as random as it can get</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>name</code></td>
|
||||||
|
<td>A to Z</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-date</code></td>
|
||||||
|
<td>newest to oldest</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>creation-time</code></td>
|
||||||
|
<td>alias of <code>creation-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-login-date</code></td>
|
||||||
|
<td>recently active first</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>last-login-time</code></td>
|
||||||
|
<td>alias of <code>last-login-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>login-date</code></td>
|
||||||
|
<td>alias of <code>last-login-date</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>login-time</code></td>
|
||||||
|
<td>alias of <code>last-login-date</code></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p><strong>Special tokens</strong></p>
|
||||||
|
|
||||||
|
<p>None.</p>
|
51
client/html/help_tos.tpl
Normal file
51
client/html/help_tos.tpl
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<p>By accessing <%- ctx.name %> (“Site”) you agree to the following
|
||||||
|
Terms of Service. If you do not agree to these terms, then please do not access
|
||||||
|
the Site.</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>The Site is presented to you AS IS, without any warranty, express or
|
||||||
|
implied. You will not hold the Site or its staff members liable for damages
|
||||||
|
caused by the use of the site.</li>
|
||||||
|
<li>The Site reserves the right to delete or modify your account, or any
|
||||||
|
content you have posted to the site.</li>
|
||||||
|
<li>The Site reserves the right to change these Terms of Service without
|
||||||
|
prior notice.</li>
|
||||||
|
<li>If you are a minor, then you will not use the Site.</li>
|
||||||
|
<li>You are using the Site only for personal use.</li>
|
||||||
|
<li>You will not spam, troll or offend anyone.</li>
|
||||||
|
<li>You accept that the Site is not liable for any content that you may
|
||||||
|
stumble upon.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class='section' id='section-prohibited-content'><strong>Prohibited content</strong></p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Child pornography: any photograph or photorealistic drawing or movie
|
||||||
|
that depicts children in a sexual manner. This includes nudity, explicit
|
||||||
|
sex, implied sex, or sexually persuasive positions.</li>
|
||||||
|
|
||||||
|
<li>Bestiality: any photograph or photorealistic drawing or movie that
|
||||||
|
depicts humans having sex (either explicit or implied) with other non-human
|
||||||
|
animals.</li>
|
||||||
|
|
||||||
|
<li>Any depiction of extreme mutilation, extreme bodily distension,
|
||||||
|
feces.</li>
|
||||||
|
|
||||||
|
<li>Personal images: any image that is suspected to be uploaded for
|
||||||
|
personal use. This includes, but is not limited to, avatars and forum
|
||||||
|
signatures.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p class='section' id='section-privacy-policy'><strong>Privacy policy</strong></p>
|
||||||
|
|
||||||
|
<p>The Site will not disclose the IP address or email address of any user
|
||||||
|
except to the staff.</p>
|
||||||
|
|
||||||
|
<p>Posts, comments, favorites, ratings and other actions linked to your account
|
||||||
|
will be stored in the Site’s database. The “Upload
|
||||||
|
anonymously” option allows you to post content without linking it to your
|
||||||
|
account – meaning your nickname will not be stored in the database
|
||||||
|
nor shown in the “Uploader” field.</p>
|
||||||
|
|
||||||
|
<p>Cookies are used to store your session data in order to keep you logged in
|
||||||
|
and personalize your web experience.</p>
|
16
client/html/home.tpl
Normal file
16
client/html/home.tpl
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<div class='content-wrapper transparent' id='home'>
|
||||||
|
<div class='messages'></div>
|
||||||
|
<header>
|
||||||
|
<h1><%- ctx.name %></h1>
|
||||||
|
</header>
|
||||||
|
<% if (ctx.canListPosts) { %>
|
||||||
|
<form class='horizontal'>
|
||||||
|
<%= ctx.makeTextInput({name: 'search-text', placeholder: 'enter some tags'}) %>
|
||||||
|
<input type='submit' value='Search'/>
|
||||||
|
<span class=sep>or</span>
|
||||||
|
<a href='<%- ctx.formatClientLink('posts') %>'>browse all posts</a>
|
||||||
|
</form>
|
||||||
|
<% } %>
|
||||||
|
<div class='post-info-container'></div>
|
||||||
|
<footer class='footer-container'></footer>
|
||||||
|
</div>
|
7
client/html/home_featured_post.tpl
Normal file
7
client/html/home_featured_post.tpl
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<div class='post-container'></div>
|
||||||
|
<% if (ctx.featuredPost) { %>
|
||||||
|
<aside>
|
||||||
|
Featured post: <%= ctx.makePostLink(ctx.featuredPost.id, true) %>,<wbr>
|
||||||
|
posted <%= ctx.makeRelativeTime(ctx.featuredPost.creationTime) %> by <%= ctx.makeUserLink(ctx.featuredPost.user) %>
|
||||||
|
</aside>
|
||||||
|
<% } %>
|
7
client/html/home_footer.tpl
Normal file
7
client/html/home_footer.tpl
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<ul>
|
||||||
|
<li><%- ctx.postCount %> posts</li><span class='sep'>
|
||||||
|
</span><li><%= ctx.makeFileSize(ctx.diskUsage) %></li><span class='sep'>
|
||||||
|
</span><li>Build <a class='version' href='https://github.com/rr-/szurubooru/commits/master'><%- ctx.version %></a><%- ctx.isDevelopmentMode ? " (DEV MODE)" : "" %> from <%= ctx.makeRelativeTime(ctx.buildDate) %></li><span class='sep'>
|
||||||
|
</span><% if (ctx.canListSnapshots) { %><li><a href='<%- ctx.formatClientLink('history') %>'>History</a></li><span class='sep'>
|
||||||
|
</span><% } %>
|
||||||
|
</ul>
|
32
client/html/index.htm
Normal file
32
client/html/index.htm
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset='utf-8'/>
|
||||||
|
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
||||||
|
<meta name='theme-color' content='#24aadd'/>
|
||||||
|
<meta name='apple-mobile-web-app-capable' content='yes'/>
|
||||||
|
<meta name='apple-mobile-web-app-status-bar-style' content='black'/>
|
||||||
|
<meta name='msapplication-TileColor' content='#ffffff'/>
|
||||||
|
<meta name="msapplication-TileImage" content="/img/mstile-150x150.png">
|
||||||
|
<title>Loading...</title>
|
||||||
|
<!-- Base HTML Placeholder -->
|
||||||
|
<link href='css/app.min.css' rel='stylesheet' type='text/css'/>
|
||||||
|
<link href='css/vendor.min.css' rel='stylesheet' type='text/css'/>
|
||||||
|
<link rel='shortcut icon' type='image/png' href='img/favicon.png'/>
|
||||||
|
<link rel='apple-touch-icon' sizes='180x180' href='img/apple-touch-icon.png'/>
|
||||||
|
<link rel='apple-touch-startup-image' href='img/apple-touch-startup-image-640x1136.png' media='(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)'/>
|
||||||
|
<link rel='apple-touch-startup-image' href='img/apple-touch-startup-image-750x1294.png' media='(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)'/>
|
||||||
|
<link rel='apple-touch-startup-image' href='img/apple-touch-startup-image-1242x2148.png' media='(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)'/>
|
||||||
|
<link rel='apple-touch-startup-image' href='img/apple-touch-startup-image-1125x2436.png' media='(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)'/>
|
||||||
|
<link rel='apple-touch-startup-image' href='img/apple-touch-startup-image-1536x2048.png' media='(min-device-width: 768px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)'/>
|
||||||
|
<link rel='apple-touch-startup-image' href='img/apple-touch-startup-image-1668x2224.png' media='(min-device-width: 834px) and (max-device-width: 834px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)'/>
|
||||||
|
<link rel='apple-touch-startup-image' href='img/apple-touch-startup-image-2048x2732.png' media='(min-device-width: 1024px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)'/>
|
||||||
|
<link rel='manifest' href='manifest.json'/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id='top-navigation-holder'></div>
|
||||||
|
<div id='content-holder'></div>
|
||||||
|
<script type='text/javascript' src='js/vendor.min.js'></script>
|
||||||
|
<script type='text/javascript' src='js/app.min.js'></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
36
client/html/login.tpl
Normal file
36
client/html/login.tpl
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<div class='content-wrapper' id='login'>
|
||||||
|
<h1>Log in</h1>
|
||||||
|
<form>
|
||||||
|
<ul class='input'>
|
||||||
|
<li>
|
||||||
|
<%= ctx.makeTextInput({
|
||||||
|
text: 'User name',
|
||||||
|
name: 'name',
|
||||||
|
required: true,
|
||||||
|
pattern: ctx.userNamePattern,
|
||||||
|
}) %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= ctx.makePasswordInput({
|
||||||
|
text: 'Password',
|
||||||
|
name: 'password',
|
||||||
|
required: true,
|
||||||
|
pattern: ctx.passwordPattern,
|
||||||
|
}) %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%= ctx.makeCheckbox({
|
||||||
|
text: 'Remember me',
|
||||||
|
name: 'remember-user',
|
||||||
|
}) %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class='messages'></div>
|
||||||
|
|
||||||
|
<div class='buttons'>
|
||||||
|
<input type='submit' value='Log in'/>
|
||||||
|
<a class='append' href='<%- ctx.formatClientLink('password-reset') %>'>Forgot the password?</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
6
client/html/manual_pager.tpl
Normal file
6
client/html/manual_pager.tpl
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<div class='pager'>
|
||||||
|
<div class='page-header-holder'></div>
|
||||||
|
<div class='messages'></div>
|
||||||
|
<div class='page-content-holder'></div>
|
||||||
|
<div class='page-nav'></div>
|
||||||
|
</div>
|
39
client/html/manual_pager_nav.tpl
Normal file
39
client/html/manual_pager_nav.tpl
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<nav class='buttons'>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<% if (ctx.prevPage !== ctx.currentPage) { %>
|
||||||
|
<a rel='prev' class='prev' href='<%- ctx.getClientUrlForPage(ctx.pages.get(ctx.prevPage).offset, ctx.pages.get(ctx.prevPage).limit) %>'>
|
||||||
|
<% } else { %>
|
||||||
|
<a rel='prev' class='prev disabled'>
|
||||||
|
<% } %>
|
||||||
|
<i class='fa fa-chevron-left'></i>
|
||||||
|
<span class='vim-nav-hint'>< Previous page</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<% for (let page of ctx.pages.values()) { %>
|
||||||
|
<% if (page.ellipsis) { %>
|
||||||
|
<li>…</li>
|
||||||
|
<% } else { %>
|
||||||
|
<% if (page.active) { %>
|
||||||
|
<li class='active'>
|
||||||
|
<% } else { %>
|
||||||
|
<li>
|
||||||
|
<% } %>
|
||||||
|
<a href='<%- ctx.getClientUrlForPage(page.offset, page.limit) %>'><%- page.number %></a>
|
||||||
|
</li>
|
||||||
|
<% } %>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<% if (ctx.nextPage !== ctx.currentPage) { %>
|
||||||
|
<a rel='next' class='next' href='<%- ctx.getClientUrlForPage(ctx.pages.get(ctx.nextPage).offset, ctx.pages.get(ctx.nextPage).limit) %>'>
|
||||||
|
<% } else { %>
|
||||||
|
<a rel='next' class='next disabled'>
|
||||||
|
<% } %>
|
||||||
|
<i class='fa fa-chevron-right'></i>
|
||||||
|
<span class='vim-nav-hint'>Next page ></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
5
client/html/not_found.tpl
Normal file
5
client/html/not_found.tpl
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<div class='not-found'>
|
||||||
|
<h1>Not found</h1>
|
||||||
|
<p><%- ctx.path %> is not a valid URL.</p>
|
||||||
|
<p><a href='<%- ctx.formatClientLink() %>'>Back to main page</a></p>
|
||||||
|
</div>
|
30
client/html/password_reset.tpl
Normal file
30
client/html/password_reset.tpl
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<div class='content-wrapper' id='password-reset'>
|
||||||
|
<h1>Password reset</h1>
|
||||||
|
<% if (ctx.canSendMails) { %>
|
||||||
|
<form autocomplete='off'>
|
||||||
|
<ul class='input'>
|
||||||
|
<li>
|
||||||
|
<%= ctx.makeTextInput({
|
||||||
|
text: 'User name or e-mail address',
|
||||||
|
name: 'user-name',
|
||||||
|
required: true,
|
||||||
|
}) %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p><small>Proceeding will send an e-mail that contains a password reset
|
||||||
|
link. Clicking it is going to generate a new password for your account.
|
||||||
|
It is recommended to change that password to something else.</small></p>
|
||||||
|
|
||||||
|
<div class='messages'></div>
|
||||||
|
<div class='buttons'>
|
||||||
|
<input type='submit' value='Proceed'/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<% } else { %>
|
||||||
|
<p>We do not support automatic password resetting.</p>
|
||||||
|
<% if (ctx.contactEmail) { %>
|
||||||
|
<p>Please send an e-mail to <a href='mailto:<%- ctx.contactEmail %>'><%- ctx.contactEmail %></a> to go through a manual procedure.</p>
|
||||||
|
<% } %>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
18
client/html/pool.tpl
Normal file
18
client/html/pool.tpl
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<div class='content-wrapper' id='pool'>
|
||||||
|
<h1><%- ctx.getPrettyName(ctx.pool.names[0]) %></h1>
|
||||||
|
<nav class='buttons'><!--
|
||||||
|
--><ul><!--
|
||||||
|
--><li data-name='summary'><a href='<%- ctx.formatClientLink('pool', ctx.pool.id) %>'>Summary</a></li><!--
|
||||||
|
--><% if (ctx.canEditAnything) { %><!--
|
||||||
|
--><li data-name='edit'><a href='<%- ctx.formatClientLink('pool', ctx.pool.id, 'edit') %>'>Edit</a></li><!--
|
||||||
|
--><% } %><!--
|
||||||
|
--><% if (ctx.canMerge) { %><!--
|
||||||
|
--><li data-name='merge'><a href='<%- ctx.formatClientLink('pool', ctx.pool.id, 'merge') %>'>Merge with…</a></li><!--
|
||||||
|
--><% } %><!--
|
||||||
|
--><% if (ctx.canDelete) { %><!--
|
||||||
|
--><li data-name='delete'><a href='<%- ctx.formatClientLink('pool', ctx.pool.id, 'delete') %>'>Delete</a></li><!--
|
||||||
|
--><% } %><!--
|
||||||
|
--></ul><!--
|
||||||
|
--></nav>
|
||||||
|
<div class='pool-content-holder'></div>
|
||||||
|
</div>
|
30
client/html/pool_categories.tpl
Normal file
30
client/html/pool_categories.tpl
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<div class='content-wrapper pool-categories'>
|
||||||
|
<form>
|
||||||
|
<h1>Pool categories</h1>
|
||||||
|
<div class="table-wrap">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class='name'>Category name</th>
|
||||||
|
<th class='color'>CSS color</th>
|
||||||
|
<th class='usages'>Usages</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% if (ctx.canCreate) { %>
|
||||||
|
<p><a href class='add'>Add new category</a></p>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<div class='messages'></div>
|
||||||
|
|
||||||
|
<% if (ctx.canCreate || ctx.canEditName || ctx.canEditColor || ctx.canDelete) { %>
|
||||||
|
<div class='buttons'>
|
||||||
|
<input type='submit' class='save' value='Save changes'>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</form>
|
||||||
|
</div>
|
43
client/html/pool_category_row.tpl
Normal file
43
client/html/pool_category_row.tpl
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<% if (ctx.poolCategory.isDefault) { %><%
|
||||||
|
%><tr data-category='<%- ctx.poolCategory.name %>' class='default'><%
|
||||||
|
%><% } else { %><%
|
||||||
|
%><tr data-category='<%- ctx.poolCategory.name %>'><%
|
||||||
|
%><% } %>
|
||||||
|
<td class='name'>
|
||||||
|
<% if (ctx.canEditName) { %>
|
||||||
|
<%= ctx.makeTextInput({value: ctx.poolCategory.name, required: true}) %>
|
||||||
|
<% } else { %>
|
||||||
|
<%- ctx.poolCategory.name %>
|
||||||
|
<% } %>
|
||||||
|
</td>
|
||||||
|
<td class='color'>
|
||||||
|
<% if (ctx.canEditColor) { %>
|
||||||
|
<%= ctx.makeColorInput({value: ctx.poolCategory.color}) %>
|
||||||
|
<% } else { %>
|
||||||
|
<%- ctx.poolCategory.color %>
|
||||||
|
<% } %>
|
||||||
|
</td>
|
||||||
|
<td class='usages'>
|
||||||
|
<% if (ctx.poolCategory.name) { %>
|
||||||
|
<a href='<%- ctx.formatClientLink('pools', {query: 'category:' + ctx.poolCategory.name}) %>'>
|
||||||
|
<%- ctx.poolCategory.poolCount %>
|
||||||
|
</a>
|
||||||
|
<% } else { %>
|
||||||
|
<%- ctx.poolCategory.poolCount %>
|
||||||
|
<% } %>
|
||||||
|
</td>
|
||||||
|
<% if (ctx.canDelete) { %>
|
||||||
|
<td class='remove'>
|
||||||
|
<% if (ctx.poolCategory.poolCount) { %>
|
||||||
|
<a class='inactive' title="Can't delete category in use">Remove</a>
|
||||||
|
<% } else { %>
|
||||||
|
<a href>Remove</a>
|
||||||
|
<% } %>
|
||||||
|
</td>
|
||||||
|
<% } %>
|
||||||
|
<% if (ctx.canSetDefault) { %>
|
||||||
|
<td class='set-default'>
|
||||||
|
<a href>Make default</a>
|
||||||
|
</td>
|
||||||
|
<% } %>
|
||||||
|
</tr>
|
42
client/html/pool_create.tpl
Normal file
42
client/html/pool_create.tpl
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<div class='content-wrapper pool-create'>
|
||||||
|
<form>
|
||||||
|
<ul class='input'>
|
||||||
|
<li class='names'>
|
||||||
|
<%= ctx.makeTextInput({
|
||||||
|
text: 'Names',
|
||||||
|
value: '',
|
||||||
|
required: true,
|
||||||
|
}) %>
|
||||||
|
</li>
|
||||||
|
<li class='category'>
|
||||||
|
<%= ctx.makeSelect({
|
||||||
|
text: 'Category',
|
||||||
|
keyValues: ctx.categories,
|
||||||
|
selectedKey: 'default',
|
||||||
|
required: true,
|
||||||
|
}) %>
|
||||||
|
</li>
|
||||||
|
<li class='description'>
|
||||||
|
<%= ctx.makeTextarea({
|
||||||
|
text: 'Description',
|
||||||
|
value: '',
|
||||||
|
}) %>
|
||||||
|
</li>
|
||||||
|
<li class='posts'>
|
||||||
|
<%= ctx.makeTextInput({
|
||||||
|
text: 'Posts',
|
||||||
|
value: '',
|
||||||
|
placeholder: 'space-separated post IDs',
|
||||||
|
}) %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<% if (ctx.canCreate) { %>
|
||||||
|
<div class='messages'></div>
|
||||||
|
|
||||||
|
<div class='buttons'>
|
||||||
|
<input type='submit' class='save' value='Create pool'>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</form>
|
||||||
|
</div>
|
21
client/html/pool_delete.tpl
Normal file
21
client/html/pool_delete.tpl
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<div class='pool-delete'>
|
||||||
|
<form>
|
||||||
|
<p>This pool has <a href='<%- ctx.formatClientLink('posts', {query: 'pool:' + ctx.pool.id}) %>'><%- ctx.pool.postCount %> post(s)</a>.</p>
|
||||||
|
|
||||||
|
<ul class='input'>
|
||||||
|
<li>
|
||||||
|
<%= ctx.makeCheckbox({
|
||||||
|
name: 'confirm-deletion',
|
||||||
|
text: 'I confirm that I want to delete this pool.',
|
||||||
|
required: true,
|
||||||
|
}) %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class='messages'></div>
|
||||||
|
|
||||||
|
<div class='buttons'>
|
||||||
|
<input type='submit' value='Delete pool'/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
50
client/html/pool_edit.tpl
Normal file
50
client/html/pool_edit.tpl
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<div class='content-wrapper pool-edit'>
|
||||||
|
<form>
|
||||||
|
<ul class='input'>
|
||||||
|
<li class='names'>
|
||||||
|
<% if (ctx.canEditNames) { %>
|
||||||
|
<%= ctx.makeTextInput({
|
||||||
|
text: 'Names',
|
||||||
|
value: ctx.pool.names.join(' '),
|
||||||
|
required: true,
|
||||||
|
}) %>
|
||||||
|
<% } %>
|
||||||
|
</li>
|
||||||
|
<li class='category'>
|
||||||
|
<% if (ctx.canEditCategory) { %>
|
||||||
|
<%= ctx.makeSelect({
|
||||||
|
text: 'Category',
|
||||||
|
keyValues: ctx.categories,
|
||||||
|
selectedKey: ctx.pool.category,
|
||||||
|
required: true,
|
||||||
|
}) %>
|
||||||
|
<% } %>
|
||||||
|
</li>
|
||||||
|
<li class='description'>
|
||||||
|
<% if (ctx.canEditDescription) { %>
|
||||||
|
<%= ctx.makeTextarea({
|
||||||
|
text: 'Description',
|
||||||
|
value: ctx.pool.description,
|
||||||
|
}) %>
|
||||||
|
<% } %>
|
||||||
|
</li>
|
||||||
|
<li class='posts'>
|
||||||
|
<% if (ctx.canEditPosts) { %>
|
||||||
|
<%= ctx.makeTextInput({
|
||||||
|
text: 'Posts',
|
||||||
|
placeholder: 'space-separated post IDs',
|
||||||
|
value: ctx.pool.posts.map(post => post.id).join(' ')
|
||||||
|
}) %>
|
||||||
|
<% } %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<% if (ctx.canEditAnything) { %>
|
||||||
|
<div class='messages'></div>
|
||||||
|
|
||||||
|
<div class='buttons'>
|
||||||
|
<input type='submit' class='save' value='Save changes'>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</form>
|
||||||
|
</div>
|
7
client/html/pool_input.tpl
Normal file
7
client/html/pool_input.tpl
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<div class='pool-input'>
|
||||||
|
<div class='main-control'>
|
||||||
|
<input type='text' placeholder='type to add…'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class='compact-pools'></ul>
|
||||||
|
</div>
|
22
client/html/pool_merge.tpl
Normal file
22
client/html/pool_merge.tpl
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<div class='pool-merge'>
|
||||||
|
<form>
|
||||||
|
<ul class='input'>
|
||||||
|
<li class='target'>
|
||||||
|
<%= ctx.makeTextInput({name: 'target-pool', required: true, text: 'Target pool', pattern: ctx.poolNamePattern}) %>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<p>Posts in the two pools will be combined.
|
||||||
|
Category needs to be handled manually.</p>
|
||||||
|
|
||||||
|
<%= ctx.makeCheckbox({required: true, text: 'I confirm that I want to merge this pool.'}) %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class='messages'></div>
|
||||||
|
|
||||||
|
<div class='buttons'>
|
||||||
|
<input type='submit' value='Merge pool'/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
23
client/html/pool_summary.tpl
Normal file
23
client/html/pool_summary.tpl
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<div class='content-wrapper pool-summary'>
|
||||||
|
<section class='details'>
|
||||||
|
<section>
|
||||||
|
Category:
|
||||||
|
<span class='<%= ctx.makeCssName(ctx.pool.category, 'pool') %>'><%- ctx.pool.category %></span>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
Aliases:<br/>
|
||||||
|
<ul><!--
|
||||||
|
--><% for (let name of ctx.pool.names.slice(1)) { %><!--
|
||||||
|
--><li><%= ctx.makePoolLink(ctx.pool.id, false, false, ctx.pool, name) %></li><!--
|
||||||
|
--><% } %><!--
|
||||||
|
--></ul>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class='description'>
|
||||||
|
<hr/>
|
||||||
|
<%= ctx.makeMarkdown(ctx.pool.description || 'This pool has no description yet.') %>
|
||||||
|
<p>This pool has <a href='<%- ctx.formatClientLink('posts', {query: 'pool:' + ctx.pool.id}) %>'><%- ctx.pool.postCount %> post(s)</a>.</p>
|
||||||
|
</section>
|
||||||
|
</div>
|
22
client/html/pools_header.tpl
Normal file
22
client/html/pools_header.tpl
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<div class='pool-list-header'>
|
||||||
|
<form class='horizontal'>
|
||||||
|
<ul class='input'>
|
||||||
|
<li>
|
||||||
|
<%= ctx.makeTextInput({text: 'Search query', id: 'search-text', name: 'search-text', value: ctx.parameters.query}) %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class='buttons'>
|
||||||
|
<input type='submit' value='Search'/>
|
||||||
|
<a class='button append' href='<%- ctx.formatClientLink('help', 'search', 'pools') %>'>Syntax help</a>
|
||||||
|
|
||||||
|
<% if (ctx.canCreate) { %>
|
||||||
|
<a class='append' href='<%- ctx.formatClientLink('pool', 'create') %>'>Add new pool</a>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (ctx.canEditPoolCategories) { %>
|
||||||
|
<a class='append' href='<%- ctx.formatClientLink('pool-categories') %>'>Pool categories</a>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
48
client/html/pools_page.tpl
Normal file
48
client/html/pools_page.tpl
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<div class='pool-list table-wrap'>
|
||||||
|
<% if (ctx.response.results.length) { %>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<th class='names'>
|
||||||
|
<% if (ctx.parameters.query == 'sort:name' || !ctx.parameters.query) { %>
|
||||||
|
<a href='<%- ctx.formatClientLink('pools', {query: '-sort:name'}) %>'>Pool name(s)</a>
|
||||||
|
<% } else { %>
|
||||||
|
<a href='<%- ctx.formatClientLink('pools', {query: 'sort:name'}) %>'>Pool name(s)</a>
|
||||||
|
<% } %>
|
||||||
|
</th>
|
||||||
|
<th class='post-count'>
|
||||||
|
<% if (ctx.parameters.query == 'sort:post-count') { %>
|
||||||
|
<a href='<%- ctx.formatClientLink('pools', {query: '-sort:post-count'}) %>'>Post count</a>
|
||||||
|
<% } else { %>
|
||||||
|
<a href='<%- ctx.formatClientLink('pools', {query: 'sort:post-count'}) %>'>Post count</a>
|
||||||
|
<% } %>
|
||||||
|
</th>
|
||||||
|
<th class='creation-time'>
|
||||||
|
<% if (ctx.parameters.query == 'sort:creation-time') { %>
|
||||||
|
<a href='<%- ctx.formatClientLink('pools', {query: '-sort:creation-time'}) %>'>Created on</a>
|
||||||
|
<% } else { %>
|
||||||
|
<a href='<%- ctx.formatClientLink('pools', {query: 'sort:creation-time'}) %>'>Created on</a>
|
||||||
|
<% } %>
|
||||||
|
</th>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% for (let pool of ctx.response.results) { %>
|
||||||
|
<tr>
|
||||||
|
<td class='names'>
|
||||||
|
<ul>
|
||||||
|
<% for (let name of pool.names) { %>
|
||||||
|
<li><%= ctx.makePoolLink(pool.id, false, false, pool, name) %></li>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
<td class='post-count'>
|
||||||
|
<a href='<%- ctx.formatClientLink('posts', {query: 'pool:' + pool.id}) %>'><%- pool.postCount %></a>
|
||||||
|
</td>
|
||||||
|
<td class='creation-time'>
|
||||||
|
<%= ctx.makeRelativeTime(pool.creationTime) %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% } %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
34
client/html/post_content.tpl
Normal file
34
client/html/post_content.tpl
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<div class='post-content post-type-<%- ctx.post.type %>'>
|
||||||
|
<% if (['image', 'animation'].includes(ctx.post.type)) { %>
|
||||||
|
|
||||||
|
<img class='resize-listener' alt='' src='<%- ctx.post.contentUrl %>'/>
|
||||||
|
|
||||||
|
<% } else if (ctx.post.type === 'flash') { %>
|
||||||
|
|
||||||
|
<object class='resize-listener' width='<%- ctx.post.canvasWidth %>' height='<%- ctx.post.canvasHeight %>' data='<%- ctx.post.contentUrl %>'>
|
||||||
|
<param name='wmode' value='opaque'/>
|
||||||
|
<param name='movie' value='<%- ctx.post.contentUrl %>'/>
|
||||||
|
</object>
|
||||||
|
|
||||||
|
<% } else if (ctx.post.type === 'video') { %>
|
||||||
|
|
||||||
|
<%= ctx.makeElement(
|
||||||
|
'video', {
|
||||||
|
class: 'resize-listener',
|
||||||
|
controls: true,
|
||||||
|
loop: (ctx.post.flags || []).includes('loop'),
|
||||||
|
playsinline: true,
|
||||||
|
autoplay: ctx.autoplay,
|
||||||
|
},
|
||||||
|
ctx.makeElement('source', {
|
||||||
|
type: ctx.post.mimeType,
|
||||||
|
src: ctx.post.contentUrl,
|
||||||
|
}),
|
||||||
|
'Your browser doesn\'t support HTML5 videos.')
|
||||||
|
%>
|
||||||
|
|
||||||
|
<% } else { console.log(new Error('Unknown post type')); } %>
|
||||||
|
|
||||||
|
<div class='post-overlay resize-listener'>
|
||||||
|
</div>
|
||||||
|
</div>
|
12
client/html/post_detail.tpl
Normal file
12
client/html/post_detail.tpl
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<div class='content-wrapper' id='post'>
|
||||||
|
<h1>Post #<%- ctx.post.id %></h1>
|
||||||
|
<nav class='buttons'><!--
|
||||||
|
--><ul><!--
|
||||||
|
--><li><a href='<%- ctx.formatClientLink('post', ctx.post.id) %>'><i class='fa fa-reply'></i> Main view</a></li><!--
|
||||||
|
--><% if (ctx.canMerge) { %><!--
|
||||||
|
--><li data-name='merge'><a href='<%- ctx.formatClientLink('post', ctx.post.id, 'merge') %>'>Merge with…</a></li><!--
|
||||||
|
--><% } %><!--
|
||||||
|
--></ul><!--
|
||||||
|
--></nav>
|
||||||
|
<div class='post-content-holder'></div>
|
||||||
|
</div>
|
127
client/html/post_edit_sidebar.tpl
Normal file
127
client/html/post_edit_sidebar.tpl
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
<div class='edit-sidebar'>
|
||||||
|
<form autocomplete='off'>
|
||||||
|
<input type='submit' value='Save' class='submit'/>
|
||||||
|
|
||||||
|
<div class='messages'></div>
|
||||||
|
|
||||||
|
<% if (ctx.enableSafety && ctx.canEditPostSafety) { %>
|
||||||
|
<section class='safety'>
|
||||||
|
<label>Safety</label>
|
||||||
|
<div class='radio-wrapper'>
|
||||||
|
<%= ctx.makeRadio({
|
||||||
|
name: 'safety',
|
||||||
|
class: 'safety-safe',
|
||||||
|
value: 'safe',
|
||||||
|
selectedValue: ctx.post.safety,
|
||||||
|
text: 'Safe'}) %>
|
||||||
|
<%= ctx.makeRadio({
|
||||||
|
name: 'safety',
|
||||||
|
class: 'safety-sketchy',
|
||||||
|
value: 'sketchy',
|
||||||
|
selectedValue: ctx.post.safety,
|
||||||
|
text: 'Sketchy'}) %>
|
||||||
|
<%= ctx.makeRadio({
|
||||||
|
name: 'safety',
|
||||||
|
value: 'unsafe',
|
||||||
|
selectedValue: ctx.post.safety,
|
||||||
|
class: 'safety-unsafe',
|
||||||
|
text: 'Unsafe'}) %>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (ctx.canEditPostRelations) { %>
|
||||||
|
<section class='relations'>
|
||||||
|
<%= ctx.makeTextInput({
|
||||||
|
text: 'Relations',
|
||||||
|
name: 'relations',
|
||||||
|
placeholder: 'space-separated post IDs',
|
||||||
|
pattern: '^[0-9 ]*$',
|
||||||
|
value: ctx.post.relations.map(rel => rel.id).join(' '),
|
||||||
|
}) %>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (ctx.canEditPostFlags && ctx.post.type === 'video') { %>
|
||||||
|
<section class='flags'>
|
||||||
|
<label>Miscellaneous</label>
|
||||||
|
<%= ctx.makeCheckbox({
|
||||||
|
text: 'Loop video',
|
||||||
|
name: 'loop',
|
||||||
|
checked: ctx.post.flags.includes('loop'),
|
||||||
|
}) %>
|
||||||
|
<%= ctx.makeCheckbox({
|
||||||
|
text: 'Sound',
|
||||||
|
name: 'sound',
|
||||||
|
checked: ctx.post.flags.includes('sound'),
|
||||||
|
}) %>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (ctx.canEditPostSource) { %>
|
||||||
|
<section class='post-source'>
|
||||||
|
<%= ctx.makeTextarea({
|
||||||
|
text: 'Source',
|
||||||
|
value: ctx.post.source,
|
||||||
|
}) %>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (ctx.canEditPostTags) { %>
|
||||||
|
<section class='tags'>
|
||||||
|
<%= ctx.makeTextInput({}) %>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (ctx.canEditPoolPosts) { %>
|
||||||
|
<section class='pools'>
|
||||||
|
<%= ctx.makeTextInput({}) %>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (ctx.canEditPostNotes) { %>
|
||||||
|
<section class='notes'>
|
||||||
|
<a href class='add'>Add a note</a>
|
||||||
|
<%= ctx.makeTextarea({disabled: true, text: 'Content (supports Markdown)', rows: '8'}) %>
|
||||||
|
<a href class='delete inactive'>Delete selected note</a>
|
||||||
|
<% if (ctx.hasClipboard) { %>
|
||||||
|
<br/>
|
||||||
|
<a href class='copy'>Export notes to clipboard</a>
|
||||||
|
<br/>
|
||||||
|
<a href class='paste'>Import notes from clipboard</a>
|
||||||
|
<% } %>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (ctx.canEditPostContent) { %>
|
||||||
|
<section class='post-content'>
|
||||||
|
<label>Content</label>
|
||||||
|
<div class='dropper-container'></div>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (ctx.canEditPostThumbnail) { %>
|
||||||
|
<section class='post-thumbnail'>
|
||||||
|
<label>Thumbnail</label>
|
||||||
|
<div class='dropper-container'></div>
|
||||||
|
<a href>Discard custom thumbnail</a>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (ctx.canFeaturePosts || ctx.canDeletePosts || ctx.canMergePosts) { %>
|
||||||
|
<section class='management'>
|
||||||
|
<ul>
|
||||||
|
<% if (ctx.canFeaturePosts) { %>
|
||||||
|
<li><a href class='feature'>Feature this post on main page</a></li>
|
||||||
|
<% } %>
|
||||||
|
<% if (ctx.canMergePosts) { %>
|
||||||
|
<li><a href class='merge'>Merge this post with another</a></li>
|
||||||
|
<% } %>
|
||||||
|
<% if (ctx.canDeletePosts) { %>
|
||||||
|
<li><a href class='delete'>Delete this post</a></li>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
</form>
|
||||||
|
</div>
|
66
client/html/post_main.tpl
Normal file
66
client/html/post_main.tpl
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<div class='content-wrapper transparent post-view'>
|
||||||
|
<aside class='sidebar'>
|
||||||
|
<nav class='buttons'>
|
||||||
|
<article class='previous-post'>
|
||||||
|
<% if (ctx.prevPostId) { %>
|
||||||
|
<% if (ctx.editMode) { %>
|
||||||
|
<a rel='prev' href='<%= ctx.getPostEditUrl(ctx.prevPostId, ctx.parameters) %>'>
|
||||||
|
<% } else { %>
|
||||||
|
<a rel='prev' href='<%= ctx.getPostUrl(ctx.prevPostId, ctx.parameters) %>'>
|
||||||
|
<% } %>
|
||||||
|
<% } else { %>
|
||||||
|
<a rel='prev' class='inactive'>
|
||||||
|
<% } %>
|
||||||
|
<i class='fa fa-chevron-left'></i>
|
||||||
|
<span class='vim-nav-hint'>< Previous post</span>
|
||||||
|
</a>
|
||||||
|
</article>
|
||||||
|
<article class='next-post'>
|
||||||
|
<% if (ctx.nextPostId) { %>
|
||||||
|
<% if (ctx.editMode) { %>
|
||||||
|
<a rel='next' href='<%= ctx.getPostEditUrl(ctx.nextPostId, ctx.parameters) %>'>
|
||||||
|
<% } else { %>
|
||||||
|
<a rel='next' href='<%= ctx.getPostUrl(ctx.nextPostId, ctx.parameters) %>'>
|
||||||
|
<% } %>
|
||||||
|
<% } else { %>
|
||||||
|
<a rel='next' class='inactive'>
|
||||||
|
<% } %>
|
||||||
|
<i class='fa fa-chevron-right'></i>
|
||||||
|
<span class='vim-nav-hint'>Next post ></span>
|
||||||
|
</a>
|
||||||
|
</article>
|
||||||
|
<% if (ctx.canEditPosts || ctx.canDeletePosts || ctx.canFeaturePosts) { %>
|
||||||
|
<article class='edit-post'>
|
||||||
|
<% if (ctx.editMode) { %>
|
||||||
|
<a href='<%= ctx.getPostUrl(ctx.post.id, ctx.parameters) %>'>
|
||||||
|
<i class='fa fa-reply'></i>
|
||||||
|
<span class='vim-nav-hint'>Back to view mode</span>
|
||||||
|
</a>
|
||||||
|
<% } else { %>
|
||||||
|
<a href='<%= ctx.getPostEditUrl(ctx.post.id, ctx.parameters) %>'>
|
||||||
|
<i class='fa fa-pencil'></i>
|
||||||
|
<span class='vim-nav-hint'>Edit post</span>
|
||||||
|
</a>
|
||||||
|
<% } %>
|
||||||
|
</article>
|
||||||
|
<% } %>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class='sidebar-container'></div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<div class='content'>
|
||||||
|
<div class='post-container'></div>
|
||||||
|
|
||||||
|
<div class='after-mobile-controls'>
|
||||||
|
<% if (ctx.canCreateComments) { %>
|
||||||
|
<h2>Add comment</h2>
|
||||||
|
<div class='comment-form-container'></div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (ctx.canListComments) { %>
|
||||||
|
<div class='comments-container'></div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
23
client/html/post_merge.tpl
Normal file
23
client/html/post_merge.tpl
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<div class='post-merge'>
|
||||||
|
<form>
|
||||||
|
<ul class='input'>
|
||||||
|
<li class='post-mirror'>
|
||||||
|
<div class='left-post-container'></div>
|
||||||
|
<div class='right-post-container'></div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<p>Tags, relations, scores, favorites and comments will be
|
||||||
|
merged. All other properties need to be handled manually.</p>
|
||||||
|
|
||||||
|
<%= ctx.makeCheckbox({required: true, text: 'I confirm that I want to merge these posts.'}) %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class='messages'></div>
|
||||||
|
|
||||||
|
<div class='buttons'>
|
||||||
|
<input type='submit' value='Merge posts'/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
59
client/html/post_merge_side.tpl
Normal file
59
client/html/post_merge_side.tpl
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<header>
|
||||||
|
<label for='merge-id-<%- ctx.name %>'>Post #</label>
|
||||||
|
<% if (ctx.editable) { %>
|
||||||
|
<input type='text' id='merge-id-<%-ctx.name %>' pattern='^[0-9]+$' value='<%- ctx.post ? ctx.post.id : '' %>'/>
|
||||||
|
<input type='button' value='Search'/>
|
||||||
|
<% } else { %>
|
||||||
|
<input type='text' id='merge-id-<%-ctx.name %>' pattern='^[0-9]+$' value='<%- ctx.post ? ctx.post.id : '' %>' readonly/>
|
||||||
|
<% } %>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<% if (ctx.post) { %>
|
||||||
|
<div class='post-thumbnail'>
|
||||||
|
<a rel='external' href='<%- ctx.post.contentUrl %>'>
|
||||||
|
<%= ctx.makeThumbnail(ctx.post.thumbnailUrl) %>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class='target-post'>
|
||||||
|
<%= ctx.makeRadio({
|
||||||
|
required: true,
|
||||||
|
text: 'Merge to this post<br/><small>' +
|
||||||
|
ctx.makeUserLink(ctx.post.user) +
|
||||||
|
', ' +
|
||||||
|
ctx.makeRelativeTime(ctx.post.creationTime) +
|
||||||
|
'</small>',
|
||||||
|
name: 'target-post',
|
||||||
|
value: ctx.name,
|
||||||
|
}) %>
|
||||||
|
</div>
|
||||||
|
<div class='target-post-content'>
|
||||||
|
<%= ctx.makeRadio({
|
||||||
|
required: true,
|
||||||
|
text: 'Use this file<br/><small>' +
|
||||||
|
ctx.makeFileSize(ctx.post.fileSize) + ' ' +
|
||||||
|
{
|
||||||
|
'image/gif': 'GIF',
|
||||||
|
'image/jpeg': 'JPEG',
|
||||||
|
'image/png': 'PNG',
|
||||||
|
'image/webp': 'WEBP',
|
||||||
|
'image/bmp': 'BMP',
|
||||||
|
'image/avif': 'AVIF',
|
||||||
|
'image/heif': 'HEIF',
|
||||||
|
'image/heic': 'HEIC',
|
||||||
|
'video/webm': 'WEBM',
|
||||||
|
'video/mp4': 'MPEG-4',
|
||||||
|
'video/quicktime': 'MOV',
|
||||||
|
'application/x-shockwave-flash': 'SWF',
|
||||||
|
}[ctx.post.mimeType] +
|
||||||
|
' (' +
|
||||||
|
(ctx.post.canvasWidth ?
|
||||||
|
`${ctx.post.canvasWidth}x${ctx.post.canvasHeight}` :
|
||||||
|
'?') +
|
||||||
|
')</small>',
|
||||||
|
name: 'target-post-content',
|
||||||
|
value: ctx.name,
|
||||||
|
}) %>
|
||||||
|
<p>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
119
client/html/post_readonly_sidebar.tpl
Normal file
119
client/html/post_readonly_sidebar.tpl
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
<div class='readonly-sidebar'>
|
||||||
|
<article class='details'>
|
||||||
|
<section class='download'>
|
||||||
|
<a rel='external' href='<%- ctx.post.contentUrl %>'>
|
||||||
|
<i class='fa fa-download'></i><!--
|
||||||
|
--><%= ctx.makeFileSize(ctx.post.fileSize) %> <!--
|
||||||
|
--><%- {
|
||||||
|
'image/gif': 'GIF',
|
||||||
|
'image/jpeg': 'JPEG',
|
||||||
|
'image/png': 'PNG',
|
||||||
|
'image/webp': 'WEBP',
|
||||||
|
'image/bmp': 'BMP',
|
||||||
|
'image/avif': 'AVIF',
|
||||||
|
'image/heif': 'HEIF',
|
||||||
|
'image/heic': 'HEIC',
|
||||||
|
'video/webm': 'WEBM',
|
||||||
|
'video/mp4': 'MPEG-4',
|
||||||
|
'video/quicktime': 'MOV',
|
||||||
|
'application/x-shockwave-flash': 'SWF',
|
||||||
|
}[ctx.post.mimeType] %><!--
|
||||||
|
--></a>
|
||||||
|
(<%- ctx.post.canvasWidth %>x<%- ctx.post.canvasHeight %>)
|
||||||
|
<% if (ctx.post.flags.length) { %><!--
|
||||||
|
--><% if (ctx.post.flags.includes('loop')) { %><i class='fa fa-repeat'></i><% } %><!--
|
||||||
|
--><% if (ctx.post.flags.includes('sound')) { %><i class='fa fa-volume-up'></i><% } %>
|
||||||
|
<% } %>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class='upload-info'>
|
||||||
|
<%= ctx.makeUserLink(ctx.post.user) %>,
|
||||||
|
<%= ctx.makeRelativeTime(ctx.post.creationTime) %>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<% if (ctx.enableSafety) { %>
|
||||||
|
<section class='safety'>
|
||||||
|
<i class='fa fa-circle safety-<%- ctx.post.safety %>'></i><!--
|
||||||
|
--><%- ctx.post.safety[0].toUpperCase() + ctx.post.safety.slice(1) %>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<section class='zoom'>
|
||||||
|
<a href class='fit-original'>Original zoom</a> ·
|
||||||
|
<a href class='fit-width'>fit width</a> ·
|
||||||
|
<a href class='fit-height'>height</a> ·
|
||||||
|
<a href class='fit-both'>both</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<% if (ctx.post.source) { %>
|
||||||
|
<section class='source'>
|
||||||
|
Source: <% for (let i = 0; i < ctx.post.sourceSplit.length; i++) { %>
|
||||||
|
<% if (i != 0) { %>·<% } %>
|
||||||
|
<a href='<%- ctx.post.sourceSplit[i] %>' title='<%- ctx.post.sourceSplit[i] %>'><%- ctx.extractRootDomain(ctx.post.sourceSplit[i]) %></a>
|
||||||
|
<% } %>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<section class='search'>
|
||||||
|
Search on
|
||||||
|
<a href='http://iqdb.org/?url=<%- encodeURIComponent(ctx.post.fullContentUrl) %>'>IQDB</a> ·
|
||||||
|
<a href='https://danbooru.donmai.us/posts?tags=md5:<%- ctx.post.checksumMD5 %>'>Danbooru</a> ·
|
||||||
|
<a href='https://lens.google.com/uploadbyurl?url=<%- encodeURIComponent(ctx.post.fullContentUrl) %>'>Google Images</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class='social'>
|
||||||
|
<div class='score-container'></div>
|
||||||
|
|
||||||
|
<div class='fav-container'></div>
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<% if (ctx.post.relations.length) { %>
|
||||||
|
<nav class='relations'>
|
||||||
|
<h1>Relations (<%- ctx.post.relations.length %>)</h1>
|
||||||
|
<ul><!--
|
||||||
|
--><% for (let post of ctx.post.relations) { %><!--
|
||||||
|
--><li><!--
|
||||||
|
--><a href='<%= ctx.getPostUrl(post.id, ctx.parameters) %>'><!--
|
||||||
|
--><%= ctx.makeThumbnail(post.thumbnailUrl) %><!--
|
||||||
|
--></a><!--
|
||||||
|
--></li><!--
|
||||||
|
--><% } %><!--
|
||||||
|
--></ul>
|
||||||
|
</nav>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<nav class='tags'>
|
||||||
|
<h1>Tags (<%- ctx.post.tags.length %>)</h1>
|
||||||
|
<% if (ctx.post.tags.length) { %>
|
||||||
|
<ul class='compact-tags'><!--
|
||||||
|
--><% for (let tag of ctx.post.tags) { %><!--
|
||||||
|
--><li><!--
|
||||||
|
--><% if (ctx.canViewTags) { %><!--
|
||||||
|
--><a href='<%- ctx.formatClientLink('tag', tag.names[0]) %>' class='<%= ctx.makeCssName(tag.category, 'tag') %>'><!--
|
||||||
|
--><i class='fa fa-tag'></i><!--
|
||||||
|
--><% } %><!--
|
||||||
|
--><% if (ctx.canViewTags) { %><!--
|
||||||
|
--></a><!--
|
||||||
|
--><% } %><!--
|
||||||
|
--><% if (ctx.canListPosts) { %><!--
|
||||||
|
--><a href='<%- ctx.formatClientLink('posts', {query: ctx.escapeTagName(tag.names[0])}) %>' class='<%= ctx.makeCssName(tag.category, 'tag') %>'><!--
|
||||||
|
--><% } %><!--
|
||||||
|
--><%- ctx.getPrettyName(tag.names[0]) %><!--
|
||||||
|
--><% if (ctx.canListPosts) { %><!--
|
||||||
|
--></a><!--
|
||||||
|
--><% } %> <!--
|
||||||
|
--><span class='tag-usages' data-pseudo-content='<%- tag.postCount %>'></span><!--
|
||||||
|
--></li><!--
|
||||||
|
--><% } %><!--
|
||||||
|
--></ul>
|
||||||
|
<% } else { %>
|
||||||
|
<p>
|
||||||
|
No tags yet!
|
||||||
|
<% if (ctx.canEditPosts) { %>
|
||||||
|
<a href='<%= ctx.getPostEditUrl(ctx.post.id, ctx.parameters) %>'>Add some.</a>
|
||||||
|
<% } %>
|
||||||
|
</p>
|
||||||
|
<% } %>
|
||||||
|
</nav>
|
||||||
|
</div>
|
39
client/html/post_upload.tpl
Normal file
39
client/html/post_upload.tpl
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<div id='post-upload'>
|
||||||
|
<form>
|
||||||
|
<div class='dropper-container'></div>
|
||||||
|
|
||||||
|
<div class='control-strip'>
|
||||||
|
<input type='submit' value='Upload all' class='submit'/>
|
||||||
|
|
||||||
|
<span class='skip-duplicates'>
|
||||||
|
<%= ctx.makeCheckbox({
|
||||||
|
text: 'Skip duplicate',
|
||||||
|
name: 'skip-duplicates',
|
||||||
|
checked: false,
|
||||||
|
}) %>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class='always-upload-similar'>
|
||||||
|
<%= ctx.makeCheckbox({
|
||||||
|
text: 'Force upload similar',
|
||||||
|
name: 'always-upload-similar',
|
||||||
|
checked: false,
|
||||||
|
}) %>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class='pause-remain-on-error'>
|
||||||
|
<%= ctx.makeCheckbox({
|
||||||
|
text: 'Pause on error',
|
||||||
|
name: 'pause-remain-on-error',
|
||||||
|
checked: true,
|
||||||
|
}) %>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<input type='button' value='Cancel' class='cancel'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='messages'></div>
|
||||||
|
|
||||||
|
<ul class='uploadables-container'></ul>
|
||||||
|
</form>
|
||||||
|
</div>
|
96
client/html/post_upload_row.tpl
Normal file
96
client/html/post_upload_row.tpl
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
<li class='uploadable-container'>
|
||||||
|
<div class='thumbnail-wrapper'>
|
||||||
|
<% if (['image'].includes(ctx.uploadable.type)) { %>
|
||||||
|
|
||||||
|
<a href='<%= ctx.uploadable.previewUrl %>'>
|
||||||
|
<%= ctx.makeThumbnail(ctx.uploadable.previewUrl) %>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<% } else if (['video'].includes(ctx.uploadable.type)) { %>
|
||||||
|
|
||||||
|
<div class='thumbnail'>
|
||||||
|
<a href='<%= ctx.uploadable.previewUrl %>'>
|
||||||
|
<video id='video' nocontrols muted>
|
||||||
|
<source type='<%- ctx.uploadable.mimeType %>' src='<%- ctx.uploadable.previewUrl %>'/>
|
||||||
|
</video>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% } else { %>
|
||||||
|
|
||||||
|
<%= ctx.makeThumbnail(null) %>
|
||||||
|
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='uploadable'>
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href class='move-up'><i class='fa fa-chevron-up'></i></a></li>
|
||||||
|
<li><a href class='move-down'><i class='fa fa-chevron-down'></i></a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href class='remove'><i class='fa fa-remove'></i></a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<span class='filename'><%= ctx.uploadable.name %></span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class='body'>
|
||||||
|
<% if (ctx.enableSafety) { %>
|
||||||
|
<div class='safety'>
|
||||||
|
<% for (let safety of ['safe', 'sketchy', 'unsafe']) { %>
|
||||||
|
<%= ctx.makeRadio({
|
||||||
|
name: 'safety-' + ctx.uploadable.key,
|
||||||
|
value: safety,
|
||||||
|
text: safety[0].toUpperCase() + safety.substr(1),
|
||||||
|
selectedValue: ctx.uploadable.safety,
|
||||||
|
}) %>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<div class='options'>
|
||||||
|
<% if (ctx.canUploadAnonymously) { %>
|
||||||
|
<div class='anonymous'>
|
||||||
|
<%= ctx.makeCheckbox({
|
||||||
|
text: 'Upload anonymously',
|
||||||
|
name: 'anonymous',
|
||||||
|
checked: ctx.uploadable.anonymous,
|
||||||
|
readonly: ctx.uploadable.forceAnonymous,
|
||||||
|
}) %>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='messages'></div>
|
||||||
|
|
||||||
|
<% if (ctx.uploadable.lookalikes.length) { %>
|
||||||
|
<ul class='lookalikes'>
|
||||||
|
<% for (let lookalike of ctx.uploadable.lookalikes) { %>
|
||||||
|
<li>
|
||||||
|
<a class='thumbnail-wrapper' title='@<%- lookalike.post.id %>'
|
||||||
|
href='<%= ctx.canViewPosts ? ctx.getPostUrl(lookalike.post.id) : "" %>'>
|
||||||
|
<%= ctx.makeThumbnail(lookalike.post.thumbnailUrl) %>
|
||||||
|
</a>
|
||||||
|
<div class='description'>
|
||||||
|
Similar post: <%= ctx.makePostLink(lookalike.post.id, true) %>
|
||||||
|
<br/>
|
||||||
|
<%- Math.round((1-lookalike.distance) * 100) %>% match
|
||||||
|
</div>
|
||||||
|
<div class='controls'>
|
||||||
|
<%= ctx.makeCheckbox({text: 'Copy tags', name: 'copy-tags'}) %>
|
||||||
|
<br/>
|
||||||
|
<%= ctx.makeCheckbox({text: 'Add relation', name: 'add-relation'}) %>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
37
client/html/posts_header.tpl
Normal file
37
client/html/posts_header.tpl
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<div class='post-list-header'><%
|
||||||
|
%><form class='horizontal search'><%
|
||||||
|
%><%= ctx.makeTextInput({text: 'Search query', id: 'search-text', name: 'search-text', value: ctx.parameters.query}) %><%
|
||||||
|
%><wbr/><%
|
||||||
|
%><input class='mousetrap' type='submit' value='Search'/><%
|
||||||
|
%><wbr/><%
|
||||||
|
%><% if (ctx.enableSafety) { %><%
|
||||||
|
%><input data-safety=safe type='button' class='mousetrap safety safety-safe <%- ctx.settings.listPosts.safe ? '' : 'disabled' %>'/><%
|
||||||
|
%><input data-safety=sketchy type='button' class='mousetrap safety safety-sketchy <%- ctx.settings.listPosts.sketchy ? '' : 'disabled' %>'/><%
|
||||||
|
%><input data-safety=unsafe type='button' class='mousetrap safety safety-unsafe <%- ctx.settings.listPosts.unsafe ? '' : 'disabled' %>'/><%
|
||||||
|
%><% } %><%
|
||||||
|
%><wbr/><%
|
||||||
|
%><a class='mousetrap button append' href='<%- ctx.formatClientLink('help', 'search', 'posts') %>'>Syntax help</a><%
|
||||||
|
%></form><%
|
||||||
|
%><% if (ctx.canBulkEditTags) { %><%
|
||||||
|
%><form class='horizontal bulk-edit bulk-edit-tags'><%
|
||||||
|
%><span class='append hint'>Tagging with:</span><%
|
||||||
|
%><a href class='mousetrap button append open'>Mass tag</a><%
|
||||||
|
%><%= ctx.makeTextInput({name: 'tag', value: ctx.parameters.tag}) %><%
|
||||||
|
%><input class='mousetrap start' type='submit' value='Start tagging'/><%
|
||||||
|
%><a href class='mousetrap button append close'>Stop tagging</a><%
|
||||||
|
%></form><%
|
||||||
|
%><% } %><%
|
||||||
|
%><% if (ctx.enableSafety && ctx.canBulkEditSafety) { %><%
|
||||||
|
%><form class='horizontal bulk-edit bulk-edit-safety'><%
|
||||||
|
%><a href class='mousetrap button append open'>Mass edit safety</a><%
|
||||||
|
%><a href class='mousetrap button append close'>Stop editing safety</a><%
|
||||||
|
%></form><%
|
||||||
|
%><% } %><%
|
||||||
|
%><% if (ctx.canBulkDelete) { %><%
|
||||||
|
%><form class='horizontal bulk-edit bulk-edit-delete'><%
|
||||||
|
%><a href class='mousetrap button append open'>Mass delete</a><%
|
||||||
|
%><input class='mousetrap start' type='submit' value='Delete selected posts'/><%
|
||||||
|
%><a href class='mousetrap button append close'>Stop deleting</a><%
|
||||||
|
%></form><%
|
||||||
|
%><% } %><%
|
||||||
|
%></div>
|
63
client/html/posts_page.tpl
Normal file
63
client/html/posts_page.tpl
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<% if (ctx.postFlow) { %><div class='post-list post-flow'><% } else { %><div class='post-list'><% } %>
|
||||||
|
<% if (ctx.response.results.length) { %>
|
||||||
|
<ul>
|
||||||
|
<% for (let post of ctx.response.results) { %>
|
||||||
|
<li data-post-id='<%= post.id %>'>
|
||||||
|
<a class='thumbnail-wrapper <%= post.tags.length > 0 ? "tags" : "no-tags" %>'
|
||||||
|
title='@<%- post.id %> (<%- post.type %>) Tags: <%- post.tags.map(tag => '#' + tag.names[0]).join(' ') || 'none' %>'
|
||||||
|
href='<%= ctx.canViewPosts ? ctx.getPostUrl(post.id, ctx.parameters) : '' %>'>
|
||||||
|
<%= ctx.makeThumbnail(post.thumbnailUrl) %>
|
||||||
|
<span class='type' data-type='<%- post.type %>'>
|
||||||
|
<% if (post.type == 'video' || post.type == 'flash' || post.type == 'animation') { %>
|
||||||
|
<span class='icon'><i class='fa fa-film'></i></span>
|
||||||
|
<% } else { %>
|
||||||
|
<%- post.type %>
|
||||||
|
<% } %>
|
||||||
|
</span>
|
||||||
|
<% if (post.score || post.favoriteCount || post.commentCount) { %>
|
||||||
|
<span class='stats'>
|
||||||
|
<% if (post.score) { %>
|
||||||
|
<span class='icon'>
|
||||||
|
<i class='fa fa-thumbs-up'></i>
|
||||||
|
<%- post.score %>
|
||||||
|
</span>
|
||||||
|
<% } %>
|
||||||
|
<% if (post.favoriteCount) { %>
|
||||||
|
<span class='icon'>
|
||||||
|
<i class='fa fa-heart'></i>
|
||||||
|
<%- post.favoriteCount %>
|
||||||
|
</span>
|
||||||
|
<% } %>
|
||||||
|
<% if (post.commentCount) { %>
|
||||||
|
<span class='icon'>
|
||||||
|
<i class='fa fa-commenting'></i>
|
||||||
|
<%- post.commentCount %>
|
||||||
|
</span>
|
||||||
|
<% } %>
|
||||||
|
</span>
|
||||||
|
<% } %>
|
||||||
|
</a>
|
||||||
|
<span class='edit-overlay'>
|
||||||
|
<% if (ctx.canBulkEditTags && ctx.parameters && ctx.parameters.tag) { %>
|
||||||
|
<a href class='tag-flipper'>
|
||||||
|
</a>
|
||||||
|
<% } %>
|
||||||
|
<% if (ctx.canBulkEditSafety && ctx.parameters && ctx.parameters.safety) { %>
|
||||||
|
<span class='safety-flipper'>
|
||||||
|
<% for (let safety of ['safe', 'sketchy', 'unsafe']) { %>
|
||||||
|
<a href data-safety='<%- safety %>' class='safety-<%- safety %><%- post.safety === safety ? ' active' : '' %>'>
|
||||||
|
</a>
|
||||||
|
<% } %>
|
||||||
|
</span>
|
||||||
|
<% } %>
|
||||||
|
<% if (ctx.canBulkDelete && ctx.parameters && ctx.parameters.delete) { %>
|
||||||
|
<a href class='delete-flipper'>
|
||||||
|
</a>
|
||||||
|
<% } %>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<% } %>
|
||||||
|
<%= ctx.makeFlexboxAlign() %>
|
||||||
|
</ul>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
27
client/html/score.tpl
Normal file
27
client/html/score.tpl
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<% if (ctx.canScore) { %>
|
||||||
|
<a href class='upvote'>
|
||||||
|
<% if (ctx.ownScore == 1) { %>
|
||||||
|
<i class='fa fa-thumbs-up'></i>
|
||||||
|
<% } else { %>
|
||||||
|
<i class='fa fa-thumbs-o-up'></i>
|
||||||
|
<% } %>
|
||||||
|
<span class='vim-nav-hint'>upvote</span>
|
||||||
|
<span class='vim-nav-hint'>like</span>
|
||||||
|
</a>
|
||||||
|
<% } else { %>
|
||||||
|
<a class='upvote inactive'>
|
||||||
|
<i class='fa fa-thumbs-o-up'></i>
|
||||||
|
</a>
|
||||||
|
<% } %>
|
||||||
|
<span class='value'><%- ctx.score %></span>
|
||||||
|
<% if (ctx.canScore) { %>
|
||||||
|
<a href class='downvote'>
|
||||||
|
<% if (ctx.ownScore == -1) { %>
|
||||||
|
<i class='fa fa-thumbs-down'></i>
|
||||||
|
<% } else { %>
|
||||||
|
<i class='fa fa-thumbs-o-down'></i>
|
||||||
|
<% } %>
|
||||||
|
<span class='vim-nav-hint'>downvote</span>
|
||||||
|
<span class='vim-nav-hint'>dislike</span>
|
||||||
|
</a>
|
||||||
|
<% } %>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue