Compare commits
97 Commits
Author | SHA1 | Date | |
---|---|---|---|
183150a6b0 | |||
0b1fad5a2b | |||
fab8f31064 | |||
eb5ecdb04e | |||
cf3a05ab53 | |||
2bba894d67 | |||
ea3ee454b6 | |||
b54574d5ca | |||
ff00c972b1 | |||
d753f92207 | |||
6951d3da56 | |||
b73ce9684f | |||
262db62265 | |||
32ab32aead | |||
0f780ddabc | |||
93db217998 | |||
8dfbf91e38 | |||
c9f1d22054 | |||
285da1c596 | |||
784b07de14 | |||
0c18aae662 | |||
d851f8c74c | |||
0e936db771 | |||
c4e3718c01 | |||
6d00fd5972 | |||
0d824b70bd | |||
c95fe5a2f7 | |||
233b66fa68 | |||
d953e28e23 | |||
d11081fc54 | |||
762c18eb74 | |||
9caaaf51f7 | |||
67433e4a17 | |||
eb22cd90b2 | |||
3a40ea6694 | |||
23133e362c | |||
183f9c978b | |||
855c26332a | |||
d6d987f476 | |||
862cc3bdfa | |||
701fed35b2 | |||
b9831c7f60 | |||
2d9dab6591 | |||
952d77a830 | |||
6d98c4e6a8 | |||
052fc4167a | |||
65f5578a6c | |||
d9a79f46dc | |||
2b5dcc9ba9 | |||
89d25030d4 | |||
6bf5333b14 | |||
d133494e9d | |||
9b2f9911f2 | |||
e37ed06a47 | |||
e667e1f947 | |||
2abbab5305 | |||
1f35f96fe3 | |||
d0cca8cdd9 | |||
cc3ed69710 | |||
8b72a90a32 | |||
128cd64adf | |||
cda601a355 | |||
1939a6b89e | |||
8f7ed5cddc | |||
836fa17bc4 | |||
ad016e50bd | |||
ba14ae969e | |||
6c1dad2626 | |||
2d3f3998de | |||
64e3a285f9 | |||
3308e6d74f | |||
aaf8e647d6 | |||
dc6514a11f | |||
92251432bd | |||
e5faaac077 | |||
b6732d7348 | |||
425a947051 | |||
0c87b2339f | |||
3b4c57e850 | |||
500ca82b69 | |||
eb05aaa103 | |||
130bedf6ec | |||
1007faa8f5 | |||
65578a4c72 | |||
6a0370b1f5 | |||
7bf0ba4ddd | |||
c34f9f77e7 | |||
e5637fd7bf | |||
a057e31e5d | |||
2aeaa0c482 | |||
b2f49953f5 | |||
23d7b73514 | |||
7c6adb32ab | |||
601aa5a16b | |||
933118c1cd | |||
2e67a09bca | |||
a8e6763cc0 |
28
.gitignore
vendored
@ -7,8 +7,7 @@
|
||||
*_index
|
||||
*.orig
|
||||
*.swp
|
||||
.* # all hidden files...
|
||||
!.gitignore # ...except gitignore
|
||||
|
||||
|
||||
# binaries
|
||||
|
||||
@ -16,25 +15,30 @@
|
||||
*.pyo
|
||||
__pycache__
|
||||
.mypy*
|
||||
*.db
|
||||
|
||||
/tools/zerobinpaste.js
|
||||
/tools/zerobinpaste.min
|
||||
/tools/zerobinpaste
|
||||
|
||||
# files generated by setuptools
|
||||
# build and env
|
||||
|
||||
build
|
||||
0bin
|
||||
env
|
||||
venv
|
||||
*.egg-info
|
||||
dist
|
||||
*.tar.gz
|
||||
*.egg
|
||||
*.in
|
||||
|
||||
# others
|
||||
|
||||
content
|
||||
# IDE
|
||||
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
settings_local.py
|
||||
build
|
||||
*-workspace
|
||||
*_old*
|
||||
.vscode
|
||||
var
|
||||
|
||||
# JS
|
||||
|
||||
package-lock.json
|
||||
node_modules
|
||||
|
597
.pylintrc
Normal file
@ -0,0 +1,597 @@
|
||||
[MASTER]
|
||||
|
||||
# A comma-separated list of package or module names from where C extensions may
|
||||
# be loaded. Extensions are loading into the active Python interpreter and may
|
||||
# run arbitrary code.
|
||||
extension-pkg-whitelist=
|
||||
|
||||
# Specify a score threshold to be exceeded before program exits with error.
|
||||
fail-under=10
|
||||
|
||||
# Add files or directories to the blacklist. They should be base names, not
|
||||
# paths.
|
||||
ignore=CVS
|
||||
|
||||
# Add files or directories matching the regex patterns to the blacklist. The
|
||||
# regex matches against base names, not paths.
|
||||
ignore-patterns=
|
||||
|
||||
# Python code to execute, usually for sys.path manipulation such as
|
||||
# pygtk.require().
|
||||
#init-hook=
|
||||
|
||||
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
|
||||
# number of processors available to use.
|
||||
jobs=1
|
||||
|
||||
# Control the amount of potential inferred values when inferring a single
|
||||
# object. This can help the performance when dealing with large functions or
|
||||
# complex, nested conditions.
|
||||
limit-inference-results=100
|
||||
|
||||
# List of plugins (as comma separated values of python module names) to load,
|
||||
# usually to register additional checkers.
|
||||
load-plugins=
|
||||
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=yes
|
||||
|
||||
# When enabled, pylint would attempt to guess common misconfiguration and emit
|
||||
# user-friendly hints instead of false-positive error messages.
|
||||
suggestion-mode=yes
|
||||
|
||||
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
||||
# active Python interpreter and may run arbitrary code.
|
||||
unsafe-load-any-extension=no
|
||||
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# Only show warnings with the listed confidence levels. Leave empty to show
|
||||
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
|
||||
confidence=
|
||||
|
||||
# Disable the message, report, category or checker with the given id(s). You
|
||||
# can either give multiple identifiers separated by comma (,) or put this
|
||||
# option multiple times (only on the command line, not in the configuration
|
||||
# file where it should appear only once). You can also use "--disable=all" to
|
||||
# disable everything first and then reenable specific checks. For example, if
|
||||
# you want to run only the similarities checker, you can use "--disable=all
|
||||
# --enable=similarities". If you want to run only the classes checker, but have
|
||||
# no Warning level messages displayed, use "--disable=all --enable=classes
|
||||
# --disable=W".
|
||||
disable=print-statement,
|
||||
parameter-unpacking,
|
||||
unpacking-in-except,
|
||||
old-raise-syntax,
|
||||
backtick,
|
||||
long-suffix,
|
||||
old-ne-operator,
|
||||
old-octal-literal,
|
||||
import-star-module-level,
|
||||
non-ascii-bytes-literal,
|
||||
raw-checker-failed,
|
||||
bad-inline-option,
|
||||
locally-disabled,
|
||||
file-ignored,
|
||||
suppressed-message,
|
||||
useless-suppression,
|
||||
deprecated-pragma,
|
||||
use-symbolic-message-instead,
|
||||
apply-builtin,
|
||||
basestring-builtin,
|
||||
buffer-builtin,
|
||||
cmp-builtin,
|
||||
coerce-builtin,
|
||||
execfile-builtin,
|
||||
file-builtin,
|
||||
long-builtin,
|
||||
raw_input-builtin,
|
||||
reduce-builtin,
|
||||
standarderror-builtin,
|
||||
unicode-builtin,
|
||||
xrange-builtin,
|
||||
coerce-method,
|
||||
delslice-method,
|
||||
getslice-method,
|
||||
setslice-method,
|
||||
no-absolute-import,
|
||||
old-division,
|
||||
dict-iter-method,
|
||||
dict-view-method,
|
||||
next-method-called,
|
||||
metaclass-assignment,
|
||||
indexing-exception,
|
||||
raising-string,
|
||||
reload-builtin,
|
||||
oct-method,
|
||||
hex-method,
|
||||
nonzero-method,
|
||||
cmp-method,
|
||||
input-builtin,
|
||||
round-builtin,
|
||||
intern-builtin,
|
||||
unichr-builtin,
|
||||
map-builtin-not-iterating,
|
||||
zip-builtin-not-iterating,
|
||||
range-builtin-not-iterating,
|
||||
filter-builtin-not-iterating,
|
||||
using-cmp-argument,
|
||||
eq-without-hash,
|
||||
div-method,
|
||||
idiv-method,
|
||||
rdiv-method,
|
||||
exception-message-attribute,
|
||||
invalid-str-codec,
|
||||
sys-max-int,
|
||||
bad-python3-import,
|
||||
deprecated-string-function,
|
||||
deprecated-str-translate-call,
|
||||
deprecated-itertools-function,
|
||||
deprecated-types-field,
|
||||
next-method-defined,
|
||||
dict-items-not-iterating,
|
||||
dict-keys-not-iterating,
|
||||
dict-values-not-iterating,
|
||||
deprecated-operator-function,
|
||||
deprecated-urllib-function,
|
||||
xreadlines-attribute,
|
||||
deprecated-sys-function,
|
||||
exception-escape,
|
||||
comprehension-escape,
|
||||
invalid-name,
|
||||
attribute-defined-outside-init
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
# either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time (only on the command line, not in the configuration file where
|
||||
# it should appear only once). See also the "--disable" option for examples.
|
||||
enable=c-extension-no-member
|
||||
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Python expression which should return a score less than or equal to 10. You
|
||||
# have access to the variables 'error', 'warning', 'refactor', and 'convention'
|
||||
# which contain the number of messages in each category, as well as 'statement'
|
||||
# which is the total number of statements analyzed. This score is used by the
|
||||
# global evaluation report (RP0004).
|
||||
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
||||
|
||||
# Template used to display messages. This is a python new-style format string
|
||||
# used to format the message information. See doc for all details.
|
||||
#msg-template=
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, json
|
||||
# and msvs (visual studio). You can also give a reporter class, e.g.
|
||||
# mypackage.mymodule.MyReporterClass.
|
||||
output-format=text
|
||||
|
||||
# Tells whether to display a full report or only the messages.
|
||||
reports=no
|
||||
|
||||
# Activate the evaluation score.
|
||||
score=yes
|
||||
|
||||
|
||||
[REFACTORING]
|
||||
|
||||
# Maximum number of nested blocks for function / method body
|
||||
max-nested-blocks=5
|
||||
|
||||
# Complete name of functions that never returns. When checking for
|
||||
# inconsistent-return-statements if a never returning function is called then
|
||||
# it will be considered as an explicit return statement and no message will be
|
||||
# printed.
|
||||
never-returning-functions=sys.exit
|
||||
|
||||
|
||||
[TYPECHECK]
|
||||
|
||||
# List of decorators that produce context managers, such as
|
||||
# contextlib.contextmanager. Add to this list to register other decorators that
|
||||
# produce valid context managers.
|
||||
contextmanager-decorators=contextlib.contextmanager
|
||||
|
||||
# List of members which are set dynamically and missed by pylint inference
|
||||
# system, and so shouldn't trigger E1101 when accessed. Python regular
|
||||
# expressions are accepted.
|
||||
generated-members=
|
||||
|
||||
# Tells whether missing members accessed in mixin class should be ignored. A
|
||||
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||
ignore-mixin-members=yes
|
||||
|
||||
# Tells whether to warn about missing members when the owner of the attribute
|
||||
# is inferred to be None.
|
||||
ignore-none=yes
|
||||
|
||||
# This flag controls whether pylint should warn about no-member and similar
|
||||
# checks whenever an opaque object is returned when inferring. The inference
|
||||
# can return multiple potential results while evaluating a Python object, but
|
||||
# some branches might not be evaluated, which results in partial inference. In
|
||||
# that case, it might be useful to still emit no-member and other checks for
|
||||
# the rest of the inferred objects.
|
||||
ignore-on-opaque-inference=yes
|
||||
|
||||
# List of class names for which member attributes should not be checked (useful
|
||||
# for classes with dynamically set attributes). This supports the use of
|
||||
# qualified names.
|
||||
ignored-classes=optparse.Values,thread._local,_thread._local,SettingsContainer
|
||||
|
||||
# List of module names for which member attributes should not be checked
|
||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||
# and thus existing member attributes cannot be deduced by static analysis). It
|
||||
# supports qualified module names, as well as Unix pattern matching.
|
||||
ignored-modules=
|
||||
|
||||
# Show a hint with possible names when a member name was not found. The aspect
|
||||
# of finding the hint is based on edit distance.
|
||||
missing-member-hint=yes
|
||||
|
||||
# The minimum edit distance a name should have in order to be considered a
|
||||
# similar match for a missing member name.
|
||||
missing-member-hint-distance=1
|
||||
|
||||
# The total number of similar names that should be taken in consideration when
|
||||
# showing a hint for a missing member.
|
||||
missing-member-max-choices=1
|
||||
|
||||
# List of decorators that change the signature of a decorated function.
|
||||
signature-mutators=
|
||||
|
||||
|
||||
[LOGGING]
|
||||
|
||||
# The type of string formatting that logging methods do. `old` means using %
|
||||
# formatting, `new` is for `{}` formatting.
|
||||
logging-format-style=old
|
||||
|
||||
# Logging modules to check that the string format arguments are in logging
|
||||
# function parameter format.
|
||||
logging-modules=logging
|
||||
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
# Ignore comments when computing similarities.
|
||||
ignore-comments=yes
|
||||
|
||||
# Ignore docstrings when computing similarities.
|
||||
ignore-docstrings=yes
|
||||
|
||||
# Ignore imports when computing similarities.
|
||||
ignore-imports=no
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=4
|
||||
|
||||
|
||||
[FORMAT]
|
||||
|
||||
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
||||
expected-line-ending-format=
|
||||
|
||||
# Regexp for a line that is allowed to be longer than the limit.
|
||||
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
||||
|
||||
# Number of spaces of indent required inside a hanging or continued line.
|
||||
indent-after-paren=4
|
||||
|
||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||
# tab).
|
||||
indent-string=' '
|
||||
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=100
|
||||
|
||||
# Maximum number of lines in a module.
|
||||
max-module-lines=1000
|
||||
|
||||
# List of optional constructs for which whitespace checking is disabled. `dict-
|
||||
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
|
||||
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
|
||||
# `empty-line` allows space-only lines.
|
||||
no-space-check=trailing-comma,
|
||||
dict-separator
|
||||
|
||||
# Allow the body of a class to be on the same line as the declaration if body
|
||||
# contains single statement.
|
||||
single-line-class-stmt=no
|
||||
|
||||
# Allow the body of an if to be on the same line as the test if there is no
|
||||
# else.
|
||||
single-line-if-stmt=no
|
||||
|
||||
|
||||
[MISCELLANEOUS]
|
||||
|
||||
# List of note tags to take in consideration, separated by a comma.
|
||||
notes=FIXME,
|
||||
XXX,
|
||||
TODO
|
||||
|
||||
# Regular expression of note tags to take in consideration.
|
||||
#notes-rgx=
|
||||
|
||||
|
||||
[VARIABLES]
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid defining new builtins when possible.
|
||||
additional-builtins=
|
||||
|
||||
# Tells whether unused global variables should be treated as a violation.
|
||||
allow-global-unused-variables=yes
|
||||
|
||||
# List of strings which can identify a callback function by name. A callback
|
||||
# name must start or end with one of those strings.
|
||||
callbacks=cb_,
|
||||
_cb
|
||||
|
||||
# A regular expression matching the name of dummy variables (i.e. expected to
|
||||
# not be used).
|
||||
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
|
||||
|
||||
# Argument names that match this expression will be ignored. Default to name
|
||||
# with leading underscore.
|
||||
ignored-argument-names=_.*|^ignored_|^unused_
|
||||
|
||||
# Tells whether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# List of qualified module names which can have objects that can redefine
|
||||
# builtins.
|
||||
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
|
||||
|
||||
|
||||
[BASIC]
|
||||
|
||||
# Naming style matching correct argument names.
|
||||
argument-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct argument names. Overrides argument-
|
||||
# naming-style.
|
||||
#argument-rgx=
|
||||
|
||||
# Naming style matching correct attribute names.
|
||||
attr-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct attribute names. Overrides attr-naming-
|
||||
# style.
|
||||
#attr-rgx=
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma.
|
||||
bad-names=foo,
|
||||
bar,
|
||||
baz,
|
||||
toto,
|
||||
tutu,
|
||||
tata
|
||||
|
||||
# Bad variable names regexes, separated by a comma. If names match any regex,
|
||||
# they will always be refused
|
||||
bad-names-rgxs=
|
||||
|
||||
# Naming style matching correct class attribute names.
|
||||
class-attribute-naming-style=any
|
||||
|
||||
# Regular expression matching correct class attribute names. Overrides class-
|
||||
# attribute-naming-style.
|
||||
#class-attribute-rgx=
|
||||
|
||||
# Naming style matching correct class names.
|
||||
class-naming-style=PascalCase
|
||||
|
||||
# Regular expression matching correct class names. Overrides class-naming-
|
||||
# style.
|
||||
#class-rgx=
|
||||
|
||||
# Naming style matching correct constant names.
|
||||
const-naming-style=UPPER_CASE
|
||||
|
||||
# Regular expression matching correct constant names. Overrides const-naming-
|
||||
# style.
|
||||
#const-rgx=
|
||||
|
||||
# Minimum line length for functions/classes that require docstrings, shorter
|
||||
# ones are exempt.
|
||||
docstring-min-length=-1
|
||||
|
||||
# Naming style matching correct function names.
|
||||
function-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct function names. Overrides function-
|
||||
# naming-style.
|
||||
#function-rgx=
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma.
|
||||
good-names=i,
|
||||
j,
|
||||
k,
|
||||
ex,
|
||||
Run,
|
||||
_
|
||||
|
||||
# Good variable names regexes, separated by a comma. If names match any regex,
|
||||
# they will always be accepted
|
||||
good-names-rgxs=
|
||||
|
||||
# Include a hint for the correct naming format with invalid-name.
|
||||
include-naming-hint=no
|
||||
|
||||
# Naming style matching correct inline iteration names.
|
||||
inlinevar-naming-style=any
|
||||
|
||||
# Regular expression matching correct inline iteration names. Overrides
|
||||
# inlinevar-naming-style.
|
||||
#inlinevar-rgx=
|
||||
|
||||
# Naming style matching correct method names.
|
||||
method-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct method names. Overrides method-naming-
|
||||
# style.
|
||||
#method-rgx=
|
||||
|
||||
# Naming style matching correct module names.
|
||||
module-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct module names. Overrides module-naming-
|
||||
# style.
|
||||
#module-rgx=
|
||||
|
||||
# Colon-delimited sets of names that determine each other's naming style when
|
||||
# the name regexes allow several styles.
|
||||
name-group=
|
||||
|
||||
# Regular expression which should only match function or class names that do
|
||||
# not require a docstring.
|
||||
no-docstring-rgx=^_
|
||||
|
||||
# List of decorators that produce properties, such as abc.abstractproperty. Add
|
||||
# to this list to register other decorators that produce valid properties.
|
||||
# These decorators are taken in consideration only for invalid-name.
|
||||
property-classes=abc.abstractproperty
|
||||
|
||||
# Naming style matching correct variable names.
|
||||
variable-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct variable names. Overrides variable-
|
||||
# naming-style.
|
||||
#variable-rgx=
|
||||
|
||||
|
||||
[STRING]
|
||||
|
||||
# This flag controls whether inconsistent-quotes generates a warning when the
|
||||
# character used as a quote delimiter is used inconsistently within a module.
|
||||
check-quote-consistency=no
|
||||
|
||||
# This flag controls whether the implicit-str-concat should generate a warning
|
||||
# on implicit string concatenation in sequences defined over several lines.
|
||||
check-str-concat-over-line-jumps=no
|
||||
|
||||
|
||||
[SPELLING]
|
||||
|
||||
# Limits count of emitted suggestions for spelling mistakes.
|
||||
max-spelling-suggestions=4
|
||||
|
||||
# Spelling dictionary name. Available dictionaries: none. To make it work,
|
||||
# install the python-enchant package.
|
||||
spelling-dict=
|
||||
|
||||
# List of comma separated words that should not be checked.
|
||||
spelling-ignore-words=
|
||||
|
||||
# A path to a file that contains the private dictionary; one word per line.
|
||||
spelling-private-dict-file=
|
||||
|
||||
# Tells whether to store unknown words to the private dictionary (see the
|
||||
# --spelling-private-dict-file option) instead of raising a message.
|
||||
spelling-store-unknown-words=no
|
||||
|
||||
|
||||
[CLASSES]
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,
|
||||
__new__,
|
||||
setUp,
|
||||
__post_init__
|
||||
|
||||
# List of member names, which should be excluded from the protected access
|
||||
# warning.
|
||||
exclude-protected=_asdict,
|
||||
_fields,
|
||||
_replace,
|
||||
_source,
|
||||
_make
|
||||
|
||||
# List of valid names for the first argument in a class method.
|
||||
valid-classmethod-first-arg=cls
|
||||
|
||||
# List of valid names for the first argument in a metaclass class method.
|
||||
valid-metaclass-classmethod-first-arg=cls
|
||||
|
||||
|
||||
[DESIGN]
|
||||
|
||||
# Maximum number of arguments for function / method.
|
||||
max-args=5
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=7
|
||||
|
||||
# Maximum number of boolean expressions in an if statement (see R0916).
|
||||
max-bool-expr=5
|
||||
|
||||
# Maximum number of branch for function / method body.
|
||||
max-branches=12
|
||||
|
||||
# Maximum number of locals for function / method body.
|
||||
max-locals=15
|
||||
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=7
|
||||
|
||||
# Maximum number of public methods for a class (see R0904).
|
||||
max-public-methods=20
|
||||
|
||||
# Maximum number of return / yield for function / method body.
|
||||
max-returns=6
|
||||
|
||||
# Maximum number of statements in function / method body.
|
||||
max-statements=50
|
||||
|
||||
# Minimum number of public methods for a class (see R0903).
|
||||
min-public-methods=2
|
||||
|
||||
|
||||
[IMPORTS]
|
||||
|
||||
# List of modules that can be imported at any level, not just the top level
|
||||
# one.
|
||||
allow-any-import-level=
|
||||
|
||||
# Allow wildcard imports from modules that define __all__.
|
||||
allow-wildcard-with-all=no
|
||||
|
||||
# Analyse import fallback blocks. This can be used to support both Python 2 and
|
||||
# 3 compatible code, which means that the block might have code that exists
|
||||
# only in one or another interpreter, leading to false positives when analysed.
|
||||
analyse-fallback-blocks=no
|
||||
|
||||
# Deprecated modules which should not be used, separated by a comma.
|
||||
deprecated-modules=optparse,tkinter.tix
|
||||
|
||||
# Create a graph of external dependencies in the given file (report RP0402 must
|
||||
# not be disabled).
|
||||
ext-import-graph=
|
||||
|
||||
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||
# given file (report RP0402 must not be disabled).
|
||||
import-graph=
|
||||
|
||||
# Create a graph of internal dependencies in the given file (report RP0402 must
|
||||
# not be disabled).
|
||||
int-import-graph=
|
||||
|
||||
# Force import order to recognize a module as part of the standard
|
||||
# compatibility libraries.
|
||||
known-standard-library=
|
||||
|
||||
# Force import order to recognize a module as part of a third party library.
|
||||
known-third-party=enchant
|
||||
|
||||
# Couples of modules and preferred modules, separated by a comma.
|
||||
preferred-modules=
|
||||
|
||||
|
||||
[EXCEPTIONS]
|
||||
|
||||
# Exceptions that will emit a warning when being caught. Defaults to
|
||||
# "BaseException, Exception".
|
||||
overgeneral-exceptions=BaseException,
|
||||
Exception
|
14
LICENCE
Normal file
@ -0,0 +1,14 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
59
README.rst
@ -1,7 +1,7 @@
|
||||
0bin
|
||||
====
|
||||
0bin: a client side encrypted pastebin
|
||||
===========================================
|
||||
|
||||
0bin is a client side encrypted pastebin that can run without a database.
|
||||
0bin is a pastebin that encrypts the user content in the browsder and can run without a database.
|
||||
|
||||
* Try it: `0bin.net <http://0bin.net>`_
|
||||
* `Report a bug <https://github.com/sametmax/0bin/issues>`_
|
||||
@ -10,7 +10,7 @@
|
||||
be pasted in it. The idea is that one can (probably...) not be legally entitled
|
||||
to `moderate the pastebin content`_ as they have no way to decrypt it.
|
||||
|
||||
It's an Python implementation of the `zerobin project`_, created by sebsauvage, under the `WTF licence`_.
|
||||
It's an Python implementation of the `zerobin project`_, created by sebsauvage, under the `WTFPL licence`_.
|
||||
|
||||
For now tested with IE9, and the last opera, safari, chrome and FF.
|
||||
|
||||
@ -20,7 +20,7 @@ but in short::
|
||||
pip install zerobin
|
||||
zerobin
|
||||
|
||||
0bin runs on Python 3.7.
|
||||
0bin runs on Python 3.7+.
|
||||
|
||||
How it works
|
||||
=============
|
||||
@ -49,70 +49,29 @@ Key points:
|
||||
Other features
|
||||
======================
|
||||
|
||||
- automatic code coloration (no need to specify);
|
||||
- automatic code coloration (no need to specify the language);
|
||||
- pastebin expiration: 1 day, 1 month or never;
|
||||
- burn after reading: the paste is destroyed after the first reading;
|
||||
- clone paste: you can't edit a paste, but you can duplicate any of them;
|
||||
- code upload: if a file is too big, you can upload it instead of using copy/paste;
|
||||
- copy paste to clipboard in a click;
|
||||
- get paste short URL in a click;
|
||||
- own previous pastes history;
|
||||
- visual hash of a paste to easily tell it apart from others in a list;
|
||||
- optional command-line tool to encrypt and paste data from shell or scripts.
|
||||
|
||||
Technologies used
|
||||
==================
|
||||
|
||||
- Python_
|
||||
- `The Bottle Python Web microframework`_
|
||||
- SJCL_ (js crypto tools)
|
||||
- jQuery_
|
||||
- Bootstrap_, the Twitter HTML5/CSS3 framework
|
||||
- VizHash.js_ to create visual hashes from pastes
|
||||
- Cherrypy_ (server only)
|
||||
- `node.js`_ (for optional command-line tool only)
|
||||
- reader mode;
|
||||
|
||||
Known issues
|
||||
============
|
||||
|
||||
- 0bin uses several HTML5/CSS3 features that are not widely supported. In that case we handle the degradation as gracefully as we can.
|
||||
- The "copy to clipboard" feature is buggy under linux. It's flash, so we won't fix it. Better wait for the HTML5 clipboard API to be implemented in major browsers.
|
||||
- The pasted content size limit check is not accurate. It's just a safety net, so we think it's ok.
|
||||
- Some url shorteners and other services storing URLs break the encryption key. We will sanitize the URL as much as we can, but there is a limit to what we can do.
|
||||
|
||||
What does 0bin not implement?
|
||||
=================================
|
||||
|
||||
* Request throttling. It would be inefficient to do it at the app level, and web servers have robust implementations for it.
|
||||
* Hash collision prevention: the ratio "probability it happens/consequence seriousness" `is not worth it`_
|
||||
* Comments: it was initially planed. But comes with a lot of issues so we chose to focus on lower hanging fruits.
|
||||
|
||||
|
||||
.. _moderate the pastebin content: http://www.zdnet.com/blog/security/pastebin-to-hunt-for-hacker-pastes-anonymous-cries-censorship/11336
|
||||
.. _zerobin project: https://github.com/sebsauvage/ZeroBin/
|
||||
.. _Python: https://en.wikipedia.org/wiki/Python_(programming_language)
|
||||
.. _The Bottle Python Web microframework: http://bottlepy.org/
|
||||
.. _SJCL: http://crypto.stanford.edu/sjcl/
|
||||
.. _jQuery: http://jquery.com/
|
||||
.. _Bootstrap: http://twitter.github.com/bootstrap/
|
||||
.. _VizHash.js: https://github.com/sametmax/VizHash.js
|
||||
.. _Cherrypy: http://www.cherrypy.org/
|
||||
.. _node.js: http://nodejs.org/
|
||||
.. _is not worth it: http://stackoverflow.com/questions/201705/how-many-random-elements-before-md5-produces-collisions
|
||||
.. _WTF licence: http://en.wikipedia.org/wiki/WTFPL
|
||||
.. _WTFPL licence: http://en.wikipedia.org/wiki/WTFPL
|
||||
|
||||
Contributing
|
||||
=============
|
||||
|
||||
Please fork the project, clone your repository and add the original repo as an upstream remote to keep yours in sync.
|
||||
|
||||
For small fixes (typo and such), you can work on master.
|
||||
|
||||
For features, you should create a dedicated branch.
|
||||
|
||||
In any case, if you modify Javascript or CSS files, you shall run compress.sh afterward to provide the minified files. It requires your to have yui-compressor installed (apt-get install yui-compressor on the debian family).
|
||||
|
||||
We don't require you to rebase/merge, ordinary merging is alright.
|
||||
|
||||
Once it's ready, just request a PR.
|
||||
|
||||
We cannot accept contributions for the moment, and will ignore PR.
|
||||
|
70
compress.sh
@ -1,51 +1,57 @@
|
||||
#! /bin/bash
|
||||
|
||||
COMMAND="yui-compressor";
|
||||
command -v $COMMAND >/dev/null 2>&1 || { echo >&2 "Error: this script requires the command '$COMMAND' to be available"; exit 1; }
|
||||
python -c "import scss" || {
|
||||
echo >&2 "Error: this script requires the scss python module. pip install -r dev-requirements.txt"
|
||||
exit 1
|
||||
}
|
||||
|
||||
CURDIR=$(dirname $(readlink -f $0));
|
||||
STATICDIR=$CURDIR'/zerobin/static/'
|
||||
CSSDIR=$STATICDIR'css/'
|
||||
JSDIR=$STATICDIR'js/'
|
||||
command -v "uglifyjs" >/dev/null 2>&1 || {
|
||||
echo >&2 "Error: this script requires the command 'uglifyjs' to be available."
|
||||
exit 1
|
||||
}
|
||||
|
||||
MAIN_JS_OUTPUT=$JSDIR"main.min.js";
|
||||
ADDITIONAL_JS_OUTPUT=$JSDIR"additional.min.js";
|
||||
CURDIR=$(dirname $(readlink -f $0))
|
||||
CSSDIR=$CURDIR'/zerobin/static/css/'
|
||||
JSDIR=$CURDIR'/zerobin/static/js/'
|
||||
|
||||
MAIN_JS_OUTPUT=$JSDIR"main.min.js"
|
||||
CSS_OUTPUT=$CSSDIR"style.min.css"
|
||||
|
||||
cat /dev/null > $CSS_OUTPUT;
|
||||
cat /dev/null >$CSS_OUTPUT
|
||||
|
||||
echo "Compressing CSS..."
|
||||
|
||||
echo $'\n''/* Bootstrap */' >> $CSS_OUTPUT;
|
||||
$COMMAND $CSSDIR'bootstrap.min.css' >> $CSS_OUTPUT;
|
||||
echo $'\n''/* Prettify */' >> $CSS_OUTPUT;
|
||||
cat $CSSDIR'prettify.css' >> $CSS_OUTPUT;
|
||||
echo $'\n''/* Custom */' >> $CSS_OUTPUT;
|
||||
$COMMAND $CSSDIR'style.css' >> $CSS_OUTPUT;
|
||||
echo $'\n''/* Prettify */' >>$CSS_OUTPUT
|
||||
python -m scss $CSSDIR'prettify.css' >>$CSS_OUTPUT
|
||||
rm $CSSDIR'prettify.min.css'
|
||||
|
||||
echo $'\n''/* Desert prettify theme */' >>$CSS_OUTPUT
|
||||
python -m scss $CSSDIR'desert.css' >>$CSS_OUTPUT
|
||||
rm $CSSDIR'desert.min.css'
|
||||
|
||||
echo $'\n''/* Bootswatch bootstrap theme */' >>$CSS_OUTPUT
|
||||
python -m scss $CSSDIR'bootswatch.4.5.css' >>$CSS_OUTPUT
|
||||
rm $CSSDIR'bootswatch.4.5.min.css'
|
||||
|
||||
echo $'\n''/* Our own CSS */' >>$CSS_OUTPUT
|
||||
python -m scss $CSSDIR'style.css' >>$CSS_OUTPUT
|
||||
|
||||
echo "Compressing JS..."
|
||||
|
||||
cat /dev/null > $MAIN_JS_OUTPUT;
|
||||
cat /dev/null >$MAIN_JS_OUTPUT
|
||||
|
||||
echo $'\n''/* Vue */' >>$MAIN_JS_OUTPUT
|
||||
uglifyjs $JSDIR'vue.js' >>$MAIN_JS_OUTPUT
|
||||
|
||||
echo $'\n''/* jQuery */' >> $MAIN_JS_OUTPUT;
|
||||
cat $JSDIR'jquery-1.7.2.min.js' >> $MAIN_JS_OUTPUT;
|
||||
# strip the "use strict" statement because it will apply to all the files
|
||||
# TODO: file a bug report to SJCL to invite them to use the function syntax
|
||||
echo $'\n''/* SJCL */' >> $MAIN_JS_OUTPUT;
|
||||
cat $JSDIR'sjcl.js' | sed 's/"use strict";//' >> $MAIN_JS_OUTPUT;
|
||||
echo $'\n''/* custom */' >> $MAIN_JS_OUTPUT;
|
||||
$COMMAND $JSDIR'behavior.js' >> $MAIN_JS_OUTPUT;
|
||||
echo $'\n''/* SJCL */' >>$MAIN_JS_OUTPUT
|
||||
uglifyjs $JSDIR'sjcl.js' | sed 's/"use strict";//' >>$MAIN_JS_OUTPUT
|
||||
|
||||
cat /dev/null > $ADDITIONAL_JS_OUTPUT;
|
||||
echo $'\n''/* Our own JS */' >>$MAIN_JS_OUTPUT
|
||||
uglifyjs $JSDIR'behavior.js' >>$MAIN_JS_OUTPUT
|
||||
|
||||
echo $'\n''/* jQuery Elastic */' >> $ADDITIONAL_JS_OUTPUT;
|
||||
$COMMAND $JSDIR'jquery.elastic.source.js' >> $ADDITIONAL_JS_OUTPUT;
|
||||
echo $'\n''/* lzw */' >> $ADDITIONAL_JS_OUTPUT;
|
||||
$COMMAND $JSDIR'lzw.js' >> $ADDITIONAL_JS_OUTPUT;
|
||||
echo $'\n''/* prettify */' >> $ADDITIONAL_JS_OUTPUT;
|
||||
cat $JSDIR'prettify.min.js' >> $ADDITIONAL_JS_OUTPUT;
|
||||
echo $'\n''/* ZeroClipboard */' >> $ADDITIONAL_JS_OUTPUT;
|
||||
$COMMAND $JSDIR'ZeroClipboard.js' >> $ADDITIONAL_JS_OUTPUT;
|
||||
echo $'\n''/* Prettify */' >>$MAIN_JS_OUTPUT
|
||||
uglifyjs $JSDIR'prettify.min.js' >>$MAIN_JS_OUTPUT
|
||||
|
||||
echo "Done"
|
||||
|
||||
|
2
dev-requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
doit==0.32.0
|
||||
pyScss==1.3.7
|
46
dodo.py
Normal file
@ -0,0 +1,46 @@
|
||||
from pathlib import Path
|
||||
|
||||
import zerobin
|
||||
|
||||
|
||||
ROOT = Path(__file__).absolute().parent
|
||||
SOURCE_DIR = ROOT / "zerobin/"
|
||||
DIST_DIR = ROOT / "dist"
|
||||
|
||||
|
||||
def source_files(extensions=None):
|
||||
extensions = extensions or [".*"]
|
||||
for ext in extensions:
|
||||
for file in SOURCE_DIR.rglob(f"*{ext}"):
|
||||
if (
|
||||
not file.suffix.endswith("pyc")
|
||||
and not file.is_dir()
|
||||
and not "/." in str(file)
|
||||
):
|
||||
yield file
|
||||
|
||||
|
||||
def generate_manifest():
|
||||
extensions = " ".join(set(f"*{f.suffix}" for f in source_files()))
|
||||
(ROOT / "MANIFEST.in").write_text(f"recursive-include zerobin {extensions}")
|
||||
|
||||
|
||||
def task_compress():
|
||||
return {
|
||||
"targets": [
|
||||
str(SOURCE_DIR / "static/js/main.min.js"),
|
||||
str(SOURCE_DIR / "static/css/style.min.css"),
|
||||
],
|
||||
"file_dep": list(str(f) for f in source_files([".css", ".js"])),
|
||||
"actions": [str(ROOT / "compress.sh")],
|
||||
}
|
||||
|
||||
|
||||
def task_build():
|
||||
|
||||
return {
|
||||
"targets": [DIST_DIR / f"zerobin-{zerobin.__version__}-py3-none-any.whl"],
|
||||
"file_dep": list(str(f) for f in source_files() if ".min." not in str(f)),
|
||||
"actions": [task_compress, generate_manifest, "python setup.py bdist_wheel",],
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
CherryPy==8.9.0
|
||||
clize==4.1.1
|
||||
lockfile==0.12.2
|
||||
sigtools==2.0.2
|
||||
bottle==0.12.18
|
||||
Beaker==1.11.0
|
BIN
screenshot.png
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 41 KiB |
27
setup.cfg
Normal file
@ -0,0 +1,27 @@
|
||||
[metadata]
|
||||
name = zerobin
|
||||
version = attr: zerobin.__version__
|
||||
description = A client side encrypted pastebin
|
||||
long_description = file: README.rst
|
||||
author= Sam et Max
|
||||
keywords = pastebin, encryption,
|
||||
author_email = lesametlemax@gmail.com
|
||||
url = https://0bin.net
|
||||
|
||||
[options]
|
||||
zip_safe = False
|
||||
include_package_data = True
|
||||
packages = find:
|
||||
install_requires =
|
||||
clize==4.1.1
|
||||
lockfile==0.12.2
|
||||
bottle==0.12.18
|
||||
Beaker==1.11.0
|
||||
Paste==3.4.3
|
||||
appdirs==1.4.4
|
||||
bleach==3.1.5
|
||||
|
||||
[options.package_data]
|
||||
* = static/img/*, static/css/*, static/js/*, view/*
|
||||
hello = *.msg
|
||||
|
67
setup.py
@ -1,71 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: ai ts=4 sts=4 et sw=4 nu
|
||||
|
||||
import os
|
||||
import zerobin
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
src_directory = 'zerobin'
|
||||
|
||||
open('MANIFEST.in', 'w').write('\n'.join((
|
||||
|
||||
"include *.rst *.tx",
|
||||
"recursive-include %s *.png *.jpg *.gif *.ico" % src_directory,
|
||||
"recursive-include %s *.css *.js *.swf" % src_directory,
|
||||
"recursive-include %s *.tpl" % src_directory
|
||||
)))
|
||||
|
||||
|
||||
setup(
|
||||
|
||||
name="zerobin",
|
||||
version=zerobin.__version__,
|
||||
packages=find_packages(exclude=["libs", "libs.*"]),
|
||||
author="Sam et Max",
|
||||
author_email="lesametlemax@gmail.com",
|
||||
description="An client side encrypted pastebin",
|
||||
long_description=open('README.rst').read(),
|
||||
install_requires=[
|
||||
'cherrypy',
|
||||
'bottle',
|
||||
'clize',
|
||||
'lockfile',
|
||||
],
|
||||
include_package_data=True,
|
||||
dependency_links=[
|
||||
'http://www.subspacefield.org/security/privilege/code/privilege/'
|
||||
],
|
||||
classifiers=[
|
||||
'Programming Language :: Python',
|
||||
"Intended Audience :: Information Technology",
|
||||
"License :: OSI Approved :: zlib/libpng License",
|
||||
"Natural Language :: English",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3.4",
|
||||
],
|
||||
entry_points = {
|
||||
'console_scripts': [
|
||||
'zerobin = zerobin.cmd:main',
|
||||
]
|
||||
}
|
||||
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
|
||||
setup()
|
||||
|
||||
|
@ -1,25 +0,0 @@
|
||||
|
||||
zerobinpaste: zerobinpaste.js commander.js ../zerobin/static/js/sjcl.js ../zerobin/static/js/lzw.js
|
||||
echo '#!/usr/bin/env node' > zerobinpaste
|
||||
cat commander.js ../zerobin/static/js/sjcl.js ../zerobin/static/js/lzw.js >> zerobinpaste
|
||||
cat zerobinpaste.js >> zerobinpaste
|
||||
chmod +x zerobinpaste
|
||||
|
||||
ugly: zerobinpaste
|
||||
uglifyjs=$$(PATH="$$(npm bin):$$PATH" which uglifyjs 2>/dev/null) \
|
||||
|| { npm install uglify-js; uglifyjs=$$(PATH="$$(npm bin):$$PATH" which uglifyjs); } \
|
||||
&& sed -i 1d zerobinpaste \
|
||||
&& $${uglifyjs} -o zerobinpaste.min zerobinpaste \
|
||||
&& echo '#!/usr/bin/env node' > zerobinpaste \
|
||||
&& cat zerobinpaste.min >> zerobinpaste \
|
||||
&& chmod +x zerobinpaste
|
||||
|
||||
clean:
|
||||
rm -f zerobinpaste{,.js,.min}
|
||||
|
||||
zerobinpaste.js: zerobinpaste.coffee
|
||||
coffee=$$(PATH="$$(npm bin):$$PATH" which coffee 2>/dev/null) \
|
||||
|| { npm install coffee-script; coffee=$$(PATH="$$(npm bin):$$PATH" which coffee); } \
|
||||
&& $$coffee -c zerobinpaste.coffee
|
||||
|
||||
.PHONY: uglify
|
1118
tools/commander.js
@ -1,145 +0,0 @@
|
||||
|
||||
program
|
||||
.version('0.0.1')
|
||||
.usage('[options] [ file ... ]\n\n' + ' Paste contents of file(s) or stdin to 0bin site.')
|
||||
.option('-u, --url [url]', 'URL of a 0bin site.')
|
||||
.option('-e, --expire [period]',
|
||||
'Expiration period - one of: 1_view, 1_day (default), 1_month, never.', '1_day')
|
||||
.option('-k, --entropy [bytes]',
|
||||
'Encryption key entropy (and hence length) to use,'\
|
||||
+ ' in bytes (default: 32).\n'\
|
||||
+ ' That key will be processed by 1000 pbkdf2-sha256 iterations, not used as-is.', 32)
|
||||
.option('-c, --config [path]',
|
||||
'Path to zerobin configuration file (default: ~/.zerobinpasterc).\n'\
|
||||
+ ' Should be json-file with the same keys as can be specified on the command line.\n'\
|
||||
+ ' Example contents: {"url": "http://some-0bin.com"}', '~/.zerobinpasterc')
|
||||
.option('-n, --nocheck', 'do not check SSL certs.')
|
||||
.parse(process.argv);
|
||||
|
||||
|
||||
[http, https, url, qs, fs, path] = ['http', 'https', 'url', 'querystring', 'fs', 'path'].map(require)
|
||||
|
||||
|
||||
# Parse config file, if any
|
||||
config = program.config.replace(/^~\/+/, '')
|
||||
config = path.resolve(process.env.HOME, config)
|
||||
|
||||
try
|
||||
if fs.statSync(config).isFile()
|
||||
config = JSON.parse(fs.readFileSync(config))
|
||||
(program[k] = v) for own k, v of config
|
||||
|
||||
|
||||
# Sanity checks and option processing
|
||||
if not program.url
|
||||
console.error('ERROR: URL option must be specified.')
|
||||
process.exit(1)
|
||||
|
||||
if program.expire == '1_view'
|
||||
# "burn_after_reading" is too damn long for cli
|
||||
program.expire = 'burn_after_reading'
|
||||
|
||||
expire_opts = ['burn_after_reading', '1_day', '1_month', 'never']
|
||||
if program.expire not in expire_opts
|
||||
console.error(
|
||||
"ERROR: --expire value (provided: '#{program.expire}')"\
|
||||
+ ' must be one of: ' + expire_opts.join(', ') + "." )
|
||||
process.exit(1)
|
||||
|
||||
program.entropy = parseInt(program.entropy)
|
||||
|
||||
|
||||
# Generated key will use base64 (6b per char) charset
|
||||
# Key is not decoded for pbkdf2, so it's generated via base64 here just for convenience
|
||||
generate_key = (entropy) ->
|
||||
entropy = Math.ceil(entropy / 8.0) * 8
|
||||
key = sjcl.bitArray.clamp(
|
||||
sjcl.random.randomWords(Math.ceil(entropy / 32), 0), entropy )
|
||||
return sjcl.codec.base64.fromBits(key, 0).replace(/\=+$/, '').replace(/\//, '-')
|
||||
|
||||
|
||||
# Paste one dump and print URL, optionally prefixed with name
|
||||
paste_file = (content, name) ->
|
||||
|
||||
content = sjcl.codec.utf8String.toBits(content)
|
||||
content = sjcl.codec.base64.fromBits(content)
|
||||
# content = lzw.compress(content)
|
||||
|
||||
key = generate_key(program.entropy)
|
||||
content = sjcl.encrypt(key, content)
|
||||
content = qs.stringify
|
||||
content: content
|
||||
expiration: program.expire
|
||||
|
||||
# host.com -> http://host.com
|
||||
if not program.url.match(/^https?:\/\//)
|
||||
program.url = 'http://' + program.url.replace(/^\/+/, '')
|
||||
|
||||
proto = http
|
||||
|
||||
if program.url.match(/^https:\/\//)
|
||||
proto = https
|
||||
|
||||
req_opts = url.parse(program.url)
|
||||
req_opts.method = 'POST'
|
||||
|
||||
req_opts.headers =
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
'Content-Length': content.length
|
||||
|
||||
req_url_base = req_opts.path
|
||||
.replace(/\/paste\/create\/?$/, '').replace(/\/+$/, '')
|
||||
req_opts.path = req_url_base + '/paste/create'
|
||||
|
||||
if program.nocheck
|
||||
req_opts.rejectUnauthorized = false
|
||||
|
||||
req = proto.request req_opts, (res) ->
|
||||
req_reply = ''
|
||||
res.setEncoding('utf8')
|
||||
res.on 'data', (chunk) -> req_reply += chunk
|
||||
res.on 'end', ->
|
||||
req_reply = JSON.parse(req_reply)
|
||||
if req_reply.status != 'ok'
|
||||
console.error("ERROR: failure posting #{name} - " + req_reply.message)
|
||||
return
|
||||
|
||||
req_opts.pathname = req_url_base + '/paste/' + req_reply.paste
|
||||
req_opts.hash = key
|
||||
paste = url.format(req_opts)
|
||||
|
||||
console.log(if name then "#{name} #{paste}" else paste)
|
||||
|
||||
req.write(content)
|
||||
req.end()
|
||||
req.on 'error', (e) -> console.error(e)
|
||||
|
||||
# Seed sjcl prng from /dev/(u)random
|
||||
do (bytes=64) ->
|
||||
for src in ['/dev/urandom', '/dev/random', null]
|
||||
break if not src or fs.existsSync(src)
|
||||
if not src
|
||||
console.error( 'ERROR: Failed to seed PRNG -'\
|
||||
+ ' /dev/(u)random is unavailable, relying only on sjcl entropy sources' )
|
||||
return
|
||||
fd = fs.openSync(src, 'r')
|
||||
buff = new Buffer(bytes)
|
||||
fs.readSync(fd, buff, 0, bytes)
|
||||
fs.closeSync(fd)
|
||||
sjcl.random.addEntropy(
|
||||
(buff.readUInt32BE(n) for n in [0..bytes/4]), bytes * 8, src )
|
||||
|
||||
|
||||
# Loop over file args or read stdin
|
||||
if not program.args or not program.args.length
|
||||
process.stdin.resume()
|
||||
process.stdin.setEncoding('utf8')
|
||||
|
||||
stdin_data = ''
|
||||
process.stdin.on 'data', (chunk) -> stdin_data += chunk
|
||||
process.stdin.on 'end', -> paste_file(stdin_data)
|
||||
|
||||
else
|
||||
for file in program.args
|
||||
paste_file( fs.readFileSync(file, 'utf8'),
|
||||
if program.args.length > 1 then path.basename(file) else null )
|
@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
from zerobin.cmd import main
|
||||
|
||||
main()
|
@ -1,5 +1,5 @@
|
||||
from pathlib import Path
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__version__ = "1.0.1"
|
||||
|
||||
ROOT_DIR = Path(__file__).absolute().parent
|
||||
|
5
zerobin/__main__.py
Normal file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from zerobin.cli import main
|
||||
|
||||
main()
|
221
zerobin/cli.py
Normal file
@ -0,0 +1,221 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
"""
|
||||
Main script including runserver and delete-paste.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
|
||||
from distutils.util import strtobool
|
||||
|
||||
import zerobin
|
||||
|
||||
from zerobin.utils import (
|
||||
settings,
|
||||
SettingsValidationError,
|
||||
ensure_app_context,
|
||||
hash_password,
|
||||
)
|
||||
from zerobin.routes import get_app
|
||||
from zerobin.paste import Paste
|
||||
|
||||
|
||||
from bottle import run
|
||||
|
||||
import clize
|
||||
|
||||
|
||||
def runserver(
|
||||
*,
|
||||
host="",
|
||||
port="",
|
||||
config_dir="",
|
||||
data_dir="",
|
||||
debug=None,
|
||||
version=False,
|
||||
server="paste",
|
||||
):
|
||||
if version:
|
||||
print("0bin V%s" % settings.VERSION)
|
||||
sys.exit(0)
|
||||
|
||||
try:
|
||||
if debug is not None:
|
||||
debug = strtobool(debug)
|
||||
updated_settings, app = get_app(
|
||||
debug=debug, config_dir=config_dir, data_dir=data_dir,
|
||||
)
|
||||
except SettingsValidationError as err:
|
||||
print("Configuration error: %s" % err, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
updated_settings.HOST = host or os.environ.get(
|
||||
"ZEROBIN_HOST", updated_settings.HOST
|
||||
)
|
||||
updated_settings.PORT = port or os.environ.get(
|
||||
"ZEROBIN_PORT", updated_settings.PORT
|
||||
)
|
||||
|
||||
if updated_settings.DEBUG:
|
||||
print(
|
||||
f"Admin URL for dev: http://{updated_settings.HOST}:{updated_settings.PORT}{settings.ADMIN_URL}"
|
||||
)
|
||||
print()
|
||||
run(
|
||||
app,
|
||||
host=updated_settings.HOST,
|
||||
port=updated_settings.PORT,
|
||||
reloader=True,
|
||||
server=server,
|
||||
)
|
||||
else:
|
||||
run(app, host=settings.HOST, port=updated_settings.PORT, server=server)
|
||||
|
||||
|
||||
# The regex parse the url and separate the paste's id from the decription key
|
||||
# After the '/paste/' part, there is several caracters, identified as
|
||||
# the uuid of the paste. Followed by a '#', the decryption key of the paste.
|
||||
paste_url = re.compile("/paste/(?P<paste_id>.*)#(?P<key>.*)")
|
||||
|
||||
|
||||
def unpack_paste(paste):
|
||||
"""Take either the ID or the URL of a paste, and return its ID"""
|
||||
|
||||
try_url = paste_url.search(paste)
|
||||
|
||||
if try_url:
|
||||
return try_url.group("paste_id")
|
||||
return paste
|
||||
|
||||
|
||||
def delete_paste(*pastes, quiet=False):
|
||||
"""
|
||||
Remove pastes, given its ID or its URL
|
||||
|
||||
quiet: Don't print anything
|
||||
|
||||
pastes: List of pastes, given by ID or URL
|
||||
"""
|
||||
|
||||
for paste_uuid in map(unpack_paste, pastes):
|
||||
try:
|
||||
Paste.load(paste_uuid).delete()
|
||||
|
||||
if not quiet:
|
||||
print("Paste {} is removed".format(paste_uuid))
|
||||
|
||||
except ValueError:
|
||||
if not quiet:
|
||||
print("Paste {} doesn't exist".format(paste_uuid))
|
||||
|
||||
|
||||
def infos():
|
||||
""" Print the route to the 0bin admin.
|
||||
|
||||
The admin route is generated by zerobin so that bots won't easily
|
||||
bruteforce it. To get the full URL, simply preppend your website domain
|
||||
name to it.
|
||||
|
||||
E.G:
|
||||
|
||||
If this command prints:
|
||||
|
||||
"/admin/f1cc3972a4b933c734b37906940cf69886161492ee4eb7c1faff5d7b5e92efb8"
|
||||
|
||||
Then the admin url is:
|
||||
|
||||
"http://yourdomain.com/admin/f1cc3972a4b933c734b37906940cf69886161492ee4eb7c1faff5d7b5e92efb8"
|
||||
|
||||
Adapt "http" and "yourdomain.com" to your configuration.
|
||||
|
||||
In debug mode, the dev server will print the url when starting.
|
||||
|
||||
"""
|
||||
|
||||
ensure_app_context()
|
||||
print(f"Zerobin version: {zerobin.__version__}")
|
||||
print(f"Admin URL (to moderate pastes): {settings.ADMIN_URL}")
|
||||
print(f"Data dir (pastes and counter): {settings.DATA_DIR}")
|
||||
print(
|
||||
f"Config dir (config file, secret key, admin password and custom views): {settings.CONFIG_DIR}"
|
||||
)
|
||||
print(
|
||||
f"Static files dir (to configure apache, nging, etc.): {settings.STATIC_FILES_ROOT}"
|
||||
)
|
||||
|
||||
|
||||
def set_admin_password(password):
|
||||
""" Set the password for the admin
|
||||
|
||||
It will be stored as a scrypt hash in a file in the var dir.
|
||||
|
||||
"""
|
||||
|
||||
ensure_app_context()
|
||||
settings.ADMIN_PASSWORD_FILE.write_bytes(hash_password(password))
|
||||
|
||||
|
||||
def clean_expired_pastes(
|
||||
*, dry_run=False, verbose=False, config_dir="", data_dir="",
|
||||
):
|
||||
""" Clean expired file pastes and empty paste directories
|
||||
|
||||
This features delete files from the data dir, make sure it's safe.
|
||||
|
||||
Use "dry_run" and "verbose" options to check first
|
||||
"""
|
||||
|
||||
ensure_app_context(config_dir=config_dir, data_dir=data_dir)
|
||||
|
||||
print("Deleting expired pastes...")
|
||||
i = 0
|
||||
for p in Paste.iter_all():
|
||||
if p.has_expired:
|
||||
if not dry_run:
|
||||
p.delete()
|
||||
if verbose:
|
||||
print(p.path, "has expired")
|
||||
i += 1
|
||||
if dry_run:
|
||||
print(f"{i} pastes would have been deleted")
|
||||
else:
|
||||
print(f"{i} pastes deleted")
|
||||
|
||||
print("Deleting empty paste directories...")
|
||||
i = 0
|
||||
for p in settings.DATA_DIR.rglob("*"):
|
||||
try:
|
||||
if p.is_dir() and not next(p.iterdir(), None):
|
||||
if not dry_run:
|
||||
p.rmdir()
|
||||
if verbose:
|
||||
print(p, "is empty")
|
||||
i += 1
|
||||
except OSError as e:
|
||||
print(f'Error while processing "{p}: {e}')
|
||||
if dry_run:
|
||||
print(f"{i} directories would have been deleted")
|
||||
else:
|
||||
print(f"{i} directories deleted")
|
||||
print("Done")
|
||||
|
||||
|
||||
def main():
|
||||
subcommands = [
|
||||
runserver,
|
||||
delete_paste,
|
||||
infos,
|
||||
set_admin_password,
|
||||
clean_expired_pastes,
|
||||
]
|
||||
subcommand_names = [
|
||||
clize.util.name_py2cli(name)
|
||||
for name in clize.util.dict_from_names(subcommands).keys()
|
||||
]
|
||||
if len(sys.argv) < 2 or sys.argv[1] not in subcommand_names:
|
||||
sys.argv.insert(1, subcommand_names[0])
|
||||
clize.run(runserver, delete_paste, infos, set_admin_password, clean_expired_pastes)
|
||||
|
155
zerobin/cmd.py
@ -1,155 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
"""
|
||||
Main script including runserver and delete-paste.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
import hashlib
|
||||
import _thread as thread
|
||||
|
||||
from zerobin.utils import (
|
||||
settings,
|
||||
SettingsValidationError,
|
||||
drop_privileges,
|
||||
ensure_var_env,
|
||||
hash_password,
|
||||
)
|
||||
from zerobin.routes import get_app
|
||||
from zerobin.paste import Paste
|
||||
|
||||
from bottle import run
|
||||
|
||||
import clize
|
||||
|
||||
|
||||
def runserver(
|
||||
*,
|
||||
host="",
|
||||
port="",
|
||||
debug=None,
|
||||
user="",
|
||||
group="",
|
||||
settings_file="",
|
||||
compressed_static=None,
|
||||
version=False,
|
||||
paste_id_length=None,
|
||||
server="cherrypy",
|
||||
):
|
||||
if version:
|
||||
print("0bin V%s" % settings.VERSION)
|
||||
sys.exit(0)
|
||||
|
||||
settings.HOST = host or settings.HOST
|
||||
settings.PORT = port or settings.PORT
|
||||
settings.USER = user or settings.USER
|
||||
settings.GROUP = group or settings.GROUP
|
||||
settings.PASTE_ID_LENGTH = paste_id_length or settings.PASTE_ID_LENGTH
|
||||
settings.DEBUG = bool(debug) if debug is not None else settings.DEBUG
|
||||
|
||||
ensure_var_env()
|
||||
|
||||
try:
|
||||
_, app = get_app(debug, settings_file, compressed_static, settings=settings)
|
||||
except SettingsValidationError as err:
|
||||
print("Configuration error: %s" % err.message, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
thread.start_new_thread(drop_privileges, (settings.USER, settings.GROUP))
|
||||
|
||||
if settings.DEBUG:
|
||||
print(f"Admin URL: http://{settings.HOST}:{settings.PORT}{settings.ADMIN_URL}")
|
||||
run(
|
||||
app, host=settings.HOST, port=settings.PORT, reloader=True, server=server,
|
||||
)
|
||||
else:
|
||||
run(app, host=settings.HOST, port=settings.PORT, server=server)
|
||||
|
||||
|
||||
# The regex parse the url and separate the paste's id from the decription key
|
||||
# After the '/paste/' part, there is several caracters, identified as
|
||||
# the uuid of the paste. Followed by a '#', the decryption key of the paste.
|
||||
paste_url = re.compile("/paste/(?P<paste_id>.*)#(?P<key>.*)")
|
||||
|
||||
|
||||
def unpack_paste(paste):
|
||||
"""Take either the ID or the URL of a paste, and return its ID"""
|
||||
|
||||
try_url = paste_url.search(paste)
|
||||
|
||||
if try_url:
|
||||
return try_url.group("paste_id")
|
||||
return paste
|
||||
|
||||
|
||||
def delete_paste(*pastes, quiet=False):
|
||||
"""
|
||||
Remove pastes, given its ID or its URL
|
||||
|
||||
quiet: Don't print anything
|
||||
|
||||
pastes: List of pastes, given by ID or URL
|
||||
"""
|
||||
|
||||
for paste_uuid in map(unpack_paste, pastes):
|
||||
try:
|
||||
Paste.load(paste_uuid).delete()
|
||||
|
||||
if not quiet:
|
||||
print("Paste {} is removed".format(paste_uuid))
|
||||
|
||||
except ValueError:
|
||||
if not quiet:
|
||||
print("Paste {} doesn't exist".format(paste_uuid))
|
||||
|
||||
|
||||
def print_admin_url():
|
||||
""" Print the route to the 0bin admin.
|
||||
|
||||
The admin route is generated by zerobin so that bots won't easily
|
||||
bruteforce it. To get the full URL, simply preppend your website domain
|
||||
name to it.
|
||||
|
||||
E.G:
|
||||
|
||||
If this command prints:
|
||||
|
||||
"/admin/f1cc3972a4b933c734b37906940cf69886161492ee4eb7c1faff5d7b5e92efb8"
|
||||
|
||||
Then the admin url is:
|
||||
|
||||
"http://yourdomain.com/admin/f1cc3972a4b933c734b37906940cf69886161492ee4eb7c1faff5d7b5e92efb8"
|
||||
|
||||
Adapt "http" and "yourdomain.com" to your configuration.
|
||||
|
||||
In debug mode, the dev server will print the url when starting.
|
||||
|
||||
"""
|
||||
|
||||
ensure_var_env()
|
||||
print(settings.ADMIN_URL)
|
||||
|
||||
|
||||
def set_admin_password(password):
|
||||
""" Set the password for the admin
|
||||
|
||||
It will be stored as a scrypt hash in a file in the var dir.
|
||||
|
||||
"""
|
||||
|
||||
ensure_var_env()
|
||||
settings.ADMIN_PASSWORD_FILE.write_bytes(hash_password(password))
|
||||
|
||||
|
||||
def main():
|
||||
subcommands = [runserver, delete_paste, print_admin_url, set_admin_password]
|
||||
subcommand_names = [
|
||||
clize.util.name_py2cli(name)
|
||||
for name in clize.util.dict_from_names(subcommands).keys()
|
||||
]
|
||||
if len(sys.argv) < 2 or sys.argv[1] not in subcommand_names:
|
||||
sys.argv.insert(1, subcommand_names[0])
|
||||
clize.run(runserver, delete_paste, print_admin_url, set_admin_password)
|
||||
|
@ -1,70 +1,33 @@
|
||||
from zerobin import ROOT_DIR
|
||||
# Get error messages and auto reload.
|
||||
# Don't set this to True in production
|
||||
DEBUG = True
|
||||
|
||||
# Path to the directory that will contains all variable content, such
|
||||
# as pastes, the secret key, etc
|
||||
VAR_DIR = ROOT_DIR.parent / "var"
|
||||
# Port and host for the embedded python server
|
||||
HOST = "127.0.0.1"
|
||||
PORT = "3255"
|
||||
|
||||
# debug will get you error messages and auto reload
|
||||
# don't set this to True in production
|
||||
DEBUG = False
|
||||
|
||||
# Should the application serve static files on it's own ?
|
||||
# If yes, set the absolute path to the static files.
|
||||
# If no, set it to None
|
||||
# In dev this is handy, in prod you probably want the HTTP servers
|
||||
# to serve it, but it's OK for small traffic to set it to True in prod too.
|
||||
STATIC_FILES_ROOT = ROOT_DIR / "static"
|
||||
|
||||
# If True, will link the compressed verion of the js and css files,
|
||||
# otherwise, will use the ordinary files
|
||||
COMPRESSED_STATIC_FILES = False
|
||||
|
||||
# A tuple of absolute paths of directory where to look the template for
|
||||
# the first one will be the first to be looked into
|
||||
# if you want to override, it needs to be it a directory at the begining of
|
||||
# this tuple. By default, custom_views is meant for that purpose.
|
||||
TEMPLATE_DIRS = (
|
||||
VAR_DIR / "custom_views",
|
||||
ROOT_DIR / "views",
|
||||
# Names/links to insert in the footer.
|
||||
#
|
||||
MENU = (
|
||||
("Create paste", "/"),
|
||||
("Github", "https://github.com/Tygs/0bin"),
|
||||
("Faq", "/faq/"), # You probably want to keep this
|
||||
# Any link with "mailto:" will be escaped to limit spam, but displayed
|
||||
# correctly to the user using JS.
|
||||
("Contact", "mailto:your@email.com"),
|
||||
("Zerobin Pastebin", "https://www.0bin.net/"), # Thanks the authors :)
|
||||
)
|
||||
|
||||
# Port and host the embeded python server should be using
|
||||
# You can also specify them using the --host and --port script options
|
||||
# which have priority on these settings
|
||||
HOST = "127.0.0.1"
|
||||
PORT = "8000"
|
||||
|
||||
# User and group the server should run as. Set to None if it should be the
|
||||
# current user. Some OS don't support it and if so, it will be ignored.
|
||||
USER = None
|
||||
GROUP = None
|
||||
# Size limit of the paste content in bytes. Be careful, allowing a size too big can
|
||||
# slow down the user's browser
|
||||
MAX_SIZE = 1024 * 600
|
||||
|
||||
# Display a tiny counter for pastes created.
|
||||
# Be carreful if your site have to many pastes this can hurt your hard drive performances.
|
||||
# Refresh counter interval. Default to every minute after a paste.
|
||||
DISPLAY_COUNTER = True
|
||||
REFRESH_COUNTER = 60 * 1 # Fill this if you want to
|
||||
ADMIN_CREDENTIALS = {
|
||||
"username": None,
|
||||
"password": None,
|
||||
}
|
||||
# Refresh counter interval.
|
||||
REFRESH_COUNTER = 60 # in seconds
|
||||
|
||||
|
||||
# Names/links to insert in the menu bar.
|
||||
# Any link with "mailto:" will be escaped to prevent spam
|
||||
MENU = (
|
||||
("Home", "/"), # internal link. First link will be highlited
|
||||
("Download 0bin", "https://github.com/sametmax/0bin"), # external link
|
||||
("Faq", "/faq/"), # faq
|
||||
("Contact", "mailto:your@email.com"), # email
|
||||
)
|
||||
|
||||
# limit size of pasted text in bytes. Be careful allowing too much size can
|
||||
# slow down user's browser
|
||||
MAX_SIZE = 1024 * 500
|
||||
|
||||
# length of base64-like paste-id string in the url, int from 4 to 27 (length of sha1 digest)
|
||||
# Length of the paste-id string in the url, int from 4 to 27 (length of sha1 digest)
|
||||
# total number of unique pastes can be calculated as 2^(6*PASTE_ID_LENGTH)
|
||||
# for PASTE_ID_LENGTH=8, for example, it's 2^(6*8) = 281 474 976 710 656
|
||||
PASTE_ID_LENGTH = 8
|
||||
|
||||
|
@ -1,15 +1,14 @@
|
||||
# coding: utf-8
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
import os
|
||||
import hashlib
|
||||
import base64
|
||||
import lockfile
|
||||
import json
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from zerobin.utils import settings, to_ascii, as_unicode, safe_open as open
|
||||
import bleach
|
||||
|
||||
from zerobin.utils import settings
|
||||
|
||||
|
||||
class Paste(object):
|
||||
@ -18,7 +17,7 @@ class Paste(object):
|
||||
calculation of the expiration date.
|
||||
"""
|
||||
|
||||
DIR_CACHE = set()
|
||||
DIR_CACHE: set = set()
|
||||
|
||||
DURATIONS = {
|
||||
"1_day": 24 * 3600,
|
||||
@ -26,10 +25,20 @@ class Paste(object):
|
||||
"never": 365 * 24 * 3600 * 100,
|
||||
}
|
||||
|
||||
def __init__(self, uuid=None, uuid_length=None, content=None, expiration=None):
|
||||
def __init__(
|
||||
self,
|
||||
uuid=None,
|
||||
uuid_length=None,
|
||||
content=None,
|
||||
expiration=None,
|
||||
title="",
|
||||
btc_tip_address="",
|
||||
):
|
||||
|
||||
self.content = content
|
||||
self.expiration = self.get_expiration(expiration)
|
||||
self.title = bleach.clean(title, strip=True)[:60]
|
||||
self.btc_tip_address = bleach.clean(btc_tip_address, strip=True)[:50]
|
||||
|
||||
if not uuid:
|
||||
# generate the uuid from the decoded content by hashing it
|
||||
@ -97,15 +106,25 @@ class Paste(object):
|
||||
uuid = os.path.basename(path)
|
||||
expiration = next(paste).strip()
|
||||
content = next(paste).strip()
|
||||
try:
|
||||
metadata = json.loads(next(paste).strip())
|
||||
except (StopIteration, json.decoder.JSONDecodeError):
|
||||
metadata = {}
|
||||
if "burn_after_reading" not in expiration:
|
||||
expiration = datetime.strptime(expiration, "%Y-%m-%d %H:%M:%S.%f")
|
||||
|
||||
except StopIteration:
|
||||
raise TypeError(to_ascii("File %s is malformed" % path))
|
||||
raise TypeError("File %s is malformed" % path)
|
||||
except (IOError, OSError):
|
||||
raise ValueError(to_ascii("Can not open paste from file %s" % path))
|
||||
raise ValueError("Can not open paste from file %s" % path)
|
||||
|
||||
return Paste(uuid=uuid, expiration=expiration, content=content)
|
||||
return Paste(
|
||||
uuid=uuid,
|
||||
expiration=expiration,
|
||||
content=content,
|
||||
title=" ".join(metadata.get("title", "").split()),
|
||||
btc_tip_address=" ".join(metadata.get("btc_tip_address", "").split()),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def load(cls, uuid):
|
||||
@ -123,9 +142,8 @@ class Paste(object):
|
||||
"""
|
||||
path = settings.PASTE_FILES_ROOT
|
||||
counter_file = os.path.join(path, "counter")
|
||||
lock = lockfile.LockFile(counter_file)
|
||||
|
||||
with lock:
|
||||
with lockfile.LockFile(counter_file):
|
||||
# Read the value from the counter
|
||||
try:
|
||||
with open(counter_file, "r") as fcounter:
|
||||
@ -170,12 +188,18 @@ class Paste(object):
|
||||
if "burn_after_reading" == self.expiration:
|
||||
expiration = self.expiration + "#%s" % datetime.now() # TODO: use UTC dates
|
||||
else:
|
||||
expiration = as_unicode(self.expiration)
|
||||
expiration = str(self.expiration)
|
||||
|
||||
# write the paste
|
||||
with open(self.path, "w") as f:
|
||||
f.write(expiration + "\n")
|
||||
f.write(self.content + "\n")
|
||||
metadata = {}
|
||||
if self.title:
|
||||
metadata["title"] = self.title
|
||||
if self.btc_tip_address:
|
||||
metadata["btc_tip_address"] = self.btc_tip_address
|
||||
f.write(json.dumps(metadata) + "\n")
|
||||
|
||||
return self
|
||||
|
||||
@ -228,3 +252,19 @@ class Paste(object):
|
||||
Delete the paste file.
|
||||
"""
|
||||
os.remove(self.path)
|
||||
|
||||
@classmethod
|
||||
def iter_all(cls, on_error=lambda e: e):
|
||||
for p in settings.PASTE_FILES_ROOT.rglob("*"):
|
||||
if p.is_file() and "counter" not in str(p):
|
||||
try:
|
||||
yield Paste.load_from_file(p)
|
||||
except (TypeError, ValueError) as e:
|
||||
on_error(e)
|
||||
|
||||
@property
|
||||
def has_expired(self):
|
||||
try:
|
||||
return self.expiration < datetime.now()
|
||||
except TypeError:
|
||||
return False
|
||||
|
@ -1,433 +0,0 @@
|
||||
#! /usr/bin/python
|
||||
"""
|
||||
privilege.py
|
||||
|
||||
Copyright (c) 2009-2010, Travis H.
|
||||
|
||||
License terms: Same as those of Python itself.
|
||||
|
||||
This module is designed to implement the privilege-dropping API
|
||||
described here:
|
||||
http://www.cs.berkeley.edu/~daw/papers/setuid-usenix02.pdf
|
||||
http://www.cs.berkeley.edu/~daw/papers/setuid-login08b.pdf
|
||||
|
||||
This topic is complex so you should read at least the second paper
|
||||
before trying to understand why I'm doing this.
|
||||
|
||||
NOTE: This code does not (yet) attempt to work on Solaris and AIX.
|
||||
That is, you must have getres[ug]id and setres[ug]id to use it.
|
||||
|
||||
TODO:
|
||||
I have added setres[ug]id to python; unsure of when it will
|
||||
appear; make a future version of this will check for its presence.
|
||||
|
||||
Make interface more OO, less imperative.
|
||||
|
||||
Implement drop_priv_temp, restore_priv.
|
||||
|
||||
Implement on systems without setres[ug]id
|
||||
|
||||
Contributors:
|
||||
Kevin Gilette
|
||||
"""
|
||||
|
||||
from ctypes import *
|
||||
from ctypes.util import find_library
|
||||
import os
|
||||
import pwd
|
||||
import grp
|
||||
|
||||
# Get the name of the Operating System
|
||||
os_kernel = os.uname()[0]
|
||||
|
||||
class PrivilegeFail(Exception):
|
||||
pass
|
||||
|
||||
clib = CDLL(find_library("c"))
|
||||
|
||||
# Tested on x86-64 -- all tests pass when run as root
|
||||
__uid_t = c_uint
|
||||
__gid_t = c_uint
|
||||
|
||||
_getresuid = clib.getresuid
|
||||
|
||||
def getresuid():
|
||||
"""Get the real, effective, and saved user IDs"""
|
||||
# Create some memory locations for getresuid to write to.
|
||||
r = __uid_t()
|
||||
e = __uid_t()
|
||||
s = __uid_t()
|
||||
# Call getresuid syscall, passing by reference.
|
||||
res = _getresuid(byref(r), byref(e), byref(s))
|
||||
if res < 0: raise pythonapi.PyErr_SetFromErrno(py_object(OSError))
|
||||
# Convert to python integers.
|
||||
return r.value, e.value, s.value
|
||||
|
||||
_getresgid = clib.getresgid
|
||||
|
||||
def getresgid():
|
||||
"""Get the real, effective, and saved group IDs"""
|
||||
r = __gid_t()
|
||||
e = __gid_t()
|
||||
s = __gid_t()
|
||||
# Call getresgid syscall, passing by reference.
|
||||
res = _getresgid(byref(r), byref(e), byref(s))
|
||||
if res < 0: raise pythonapi.PyErr_SetFromErrno(py_object(OSError))
|
||||
# Convert to python integers.
|
||||
return r.value, e.value, s.value
|
||||
|
||||
# Import the setresuid system call using ctypes
|
||||
_setresuid = clib.setresuid
|
||||
_setresuid.argttypes = [__uid_t, __uid_t, __uid_t]
|
||||
_setresuid.resttype = c_int
|
||||
|
||||
# Import the setresgid system call using ctypes
|
||||
_setresgid = clib.setresgid
|
||||
_setresgid.argttypes = [__gid_t, __gid_t, __gid_t]
|
||||
_setresgid.resttype = c_int
|
||||
|
||||
def setresuid(ruid, euid, suid):
|
||||
"""Set the real, effective, and saved user IDs"""
|
||||
res = _setresuid(__uid_t(ruid), __uid_t(euid), __uid_t(suid))
|
||||
if res < 0: raise pythonapi.PyErr_SetFromErrno(py_object(OSError))
|
||||
|
||||
def setresgid(rgid, egid, sgid):
|
||||
"""Set the real, effecive, and saved group IDs"""
|
||||
res = _setresgid(__gid_t(rgid), __gid_t(egid), __gid_t(sgid))
|
||||
if res < 0: raise pythonapi.PyErr_SetFromErrno(py_object(OSError))
|
||||
|
||||
def sort_uniq(args):
|
||||
"""Sort a sequence, discarding duplicates."""
|
||||
return sorted(set(args))
|
||||
|
||||
class user_credentials:
|
||||
"""
|
||||
This represents the credentials associated with a user.
|
||||
User ID
|
||||
Group ID
|
||||
Supplementary Groups
|
||||
This is used as an argument to drop_permanently
|
||||
"""
|
||||
def __init__(self, uid, gid, sups):
|
||||
# -1 has special meaning for several set*id calls (ignore)
|
||||
if uid == -1: raise PrivilegeFail
|
||||
if gid == -1: raise PrivilegeFail
|
||||
nm = os.sysconf('SC_NGROUPS_MAX')
|
||||
if nm < 0 or nm < len(sups): raise PrivilegeFail
|
||||
self.uid = uid
|
||||
self.gid = gid
|
||||
self.sups = sort_uniq(sups)
|
||||
|
||||
def eql_sups(current, target):
|
||||
"""
|
||||
Compare two supplementary group lists, and ignore if effective GID is in current but not target.
|
||||
Prerequisite: The supplementary group lists are sorted and filtered for duplicates.
|
||||
"""
|
||||
egid = os.getegid()
|
||||
my_current = sort_uniq(current)
|
||||
# Instead of tediously ignoring this value, if it's in the current list, then go ahead
|
||||
# and add it to the target list
|
||||
if egid in current:
|
||||
my_target = target + [ egid ]
|
||||
else:
|
||||
my_target = target
|
||||
my_target = sort_uniq(my_target)
|
||||
return my_current == my_target
|
||||
|
||||
def get_sups():
|
||||
"""This is here to give us a layer of abstraction relative to system calls"""
|
||||
return os.getgroups()
|
||||
|
||||
def set_sups(target_sups):
|
||||
"""
|
||||
This is designed to give us a layer of abstraction from the system calls.
|
||||
It also accommodates FreeBSD's idiosyncrasy (which is POSIX-compliant) of
|
||||
keeping the egid in the supplementary groups list.
|
||||
It also makes an effort to not call the setgroups routine if the target
|
||||
group list is identical to the current one in force.
|
||||
"""
|
||||
global os_kernel
|
||||
if os_kernel == 'FreeBSD':
|
||||
target_sups = [ os.getegid() ] + target_sups
|
||||
if os.geteuid() == 0:
|
||||
# This will raise an OSError exception if it fails
|
||||
os.setgroups(target_sups)
|
||||
else:
|
||||
cur_sups = get_sups()
|
||||
# This will probably fail
|
||||
if not eql_sups(cur_sups, target_sups):
|
||||
# This will raise an OSError exception if it fails
|
||||
os.setgroups(target_sups)
|
||||
return True
|
||||
|
||||
def set_gids(r, e, s):
|
||||
"""This is here to give us a layer of abstraction relative to system calls"""
|
||||
setresgid(r, e, s)
|
||||
|
||||
def set_uids(r, e, s):
|
||||
"""This is here to give us a layer of abstraction relative to system calls"""
|
||||
setresuid(r, e, s)
|
||||
|
||||
class res_ids:
|
||||
"""
|
||||
This represents the three IDs (group or user) associated with a process.
|
||||
"""
|
||||
def __init__(self, real, effective, saved):
|
||||
self.r = real
|
||||
self.e = effective
|
||||
self.s = saved
|
||||
|
||||
class proc_credentials:
|
||||
"""
|
||||
This obtains and represents the credentials associated with a process.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.uids = apply(res_ids, getresuid())
|
||||
self.gids = apply(res_ids, getresgid())
|
||||
self.sups = sort_uniq(os.getgroups())
|
||||
|
||||
def get_fs_ids():
|
||||
"""Get filesystem IDs - applies only to Linux"""
|
||||
uid = None
|
||||
gid = None
|
||||
file = open('/proc/self/status', 'r')
|
||||
for line in file:
|
||||
fields = line.split()
|
||||
if fields[0] == 'Uid:':
|
||||
uid = int(fields[4])
|
||||
elif fields[0] == 'Gid:':
|
||||
gid = int(fields[4])
|
||||
return uid, gid
|
||||
|
||||
def coerce_user(user):
|
||||
if hasattr(user, '__int__'):
|
||||
return int(user)
|
||||
return pwd.getpwnam(user).pw_uid
|
||||
|
||||
def coerce_group(group):
|
||||
if hasattr(group, '__int__'):
|
||||
return int(group)
|
||||
return grp.getgrnam(group).gr_gid
|
||||
|
||||
def drop_privileges_permanently(uid, gid, sups):
|
||||
"""
|
||||
This routine is designed to permanently drop all privileges to the
|
||||
user, group, and supplementary groups specified.
|
||||
"""
|
||||
|
||||
uid = coerce_user(uid)
|
||||
gid = coerce_group(gid)
|
||||
sups = map(coerce_group, sups)
|
||||
|
||||
# This does some syntax checking
|
||||
ucred = user_credentials(uid, gid, sups)
|
||||
|
||||
# This is for our convenience
|
||||
u = uid
|
||||
g = gid
|
||||
|
||||
# Order is important in these three calls
|
||||
set_sups(ucred.sups)
|
||||
set_gids(g, g, g) # real, effective, saved
|
||||
set_uids(u, u, u) # real, effective, saved
|
||||
|
||||
# Check that we actually did what we expected or throw exception.
|
||||
pc = proc_credentials()
|
||||
# Portably compare the supplementary group list
|
||||
if not eql_sups(pc.sups, ucred.sups): raise PrivilegeFail
|
||||
# Check all the gids
|
||||
if not (g == pc.gids.r and g == pc.gids.e and g == pc.gids.s):
|
||||
raise PrivilegeFail
|
||||
# Check all the uids
|
||||
if not (u == pc.uids.r and u == pc.gids.e and u == pc.uids.s):
|
||||
raise PrivilegeFail
|
||||
global os_kernel
|
||||
if os_kernel == 'Linux':
|
||||
if get_fs_ids() != (u, g): raise PrivilegeFail
|
||||
|
||||
# This is all test code
|
||||
# It is run if this script is invoked directly
|
||||
if __name__ == '__main__':
|
||||
|
||||
import unittest
|
||||
|
||||
class test_getresXid(unittest.TestCase):
|
||||
"""Test the calls to getresXid"""
|
||||
def test__getresuid(self):
|
||||
# TODO: is there any way to avoid redefining this here? I tried a global but it didn't
|
||||
# work.
|
||||
# Note: the double-leading underscore may be getting special-cased by python.
|
||||
__uid_t = c_int
|
||||
r = __uid_t()
|
||||
e = __uid_t()
|
||||
s = __uid_t()
|
||||
ret = _getresuid(byref(r), byref(e), byref(s))
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(r.value, os.getuid())
|
||||
self.assertEqual(e.value, os.geteuid())
|
||||
# NOTE: no other portable way to get saved UID
|
||||
def test_getresuid(self):
|
||||
(r, e, s) = getresuid()
|
||||
self.assertEqual(r, os.getuid())
|
||||
self.assertEqual(e, os.geteuid())
|
||||
# NOTE: no other portable way to get saved UID
|
||||
def test__getresgid(self):
|
||||
__gid_t = c_int
|
||||
r = __gid_t()
|
||||
e = __gid_t()
|
||||
s = __gid_t()
|
||||
ret = _getresgid(byref(r), byref(e), byref(s))
|
||||
self.assertEqual(ret, 0)
|
||||
self.assertEqual(r.value, os.getgid())
|
||||
self.assertEqual(e.value, os.getegid())
|
||||
# NOTE: no other portable way to get saved GID
|
||||
def test_getresgid(self):
|
||||
(r, e, s) = getresgid()
|
||||
self.assertEqual(r, os.getgid())
|
||||
self.assertEqual(e, os.getegid())
|
||||
# NOTE: no other portable way to get saved GID
|
||||
|
||||
class test_setresuid(unittest.TestCase):
|
||||
"""Test the call to setresuid"""
|
||||
def setUp(self):
|
||||
self.uid = os.geteuid()
|
||||
def test__setresuid(self):
|
||||
__uid_t = c_int
|
||||
r1 = __uid_t(1)
|
||||
e1 = __uid_t(1)
|
||||
# Must save root UID so that we can reset UIDs for other tests
|
||||
s1 = __uid_t(0)
|
||||
if self.uid == 0:
|
||||
rv = _setresuid(r1, e1, s1)
|
||||
self.assertEqual(rv, 0)
|
||||
(r2, e2, s2) = getresuid()
|
||||
self.assertEqual(r1.value, r2)
|
||||
self.assertEqual(e1.value, e2)
|
||||
self.assertEqual(s1.value, s2)
|
||||
else:
|
||||
rv = _setresuid(r1, e1, s1)
|
||||
self.assertEqual(rv, -1)
|
||||
def test_setresuid(self):
|
||||
if self.uid == 0:
|
||||
setresuid(1,1,0)
|
||||
(r, e, s) = getresuid()
|
||||
self.assertEqual(r, 1)
|
||||
self.assertEqual(e, 1)
|
||||
self.assertEqual(s, 0)
|
||||
else:
|
||||
self.assertRaises(OSError, setresuid, 1, 1, 1)
|
||||
def tearDown(self):
|
||||
if self.uid == 0:
|
||||
# Restore UIDs for next test
|
||||
_setresuid(0, 0, 0)
|
||||
|
||||
class test_setresgid(unittest.TestCase):
|
||||
"""Test the call to setresgid"""
|
||||
def setUp(self):
|
||||
self.uid = os.geteuid()
|
||||
def test__setresgid(self):
|
||||
__gid_t = c_int
|
||||
r1 = __gid_t(1)
|
||||
e1 = __gid_t(1)
|
||||
s1 = __gid_t(1)
|
||||
if self.uid == 0:
|
||||
rv = _setresgid(r1, e1, s1)
|
||||
self.assertEqual(rv, 0)
|
||||
(r2, e2, s2) = getresgid()
|
||||
self.assertEqual(r1.value, r2)
|
||||
self.assertEqual(e1.value, e2)
|
||||
self.assertEqual(s1.value, s2)
|
||||
else:
|
||||
rv = _setresgid(r1, e1, s1)
|
||||
self.assertEqual(rv, -1)
|
||||
def test_setresgid(self):
|
||||
if self.uid == 0:
|
||||
setresgid(1,1,1)
|
||||
(r, e, s) = getresgid()
|
||||
self.assertEqual(r, 1)
|
||||
self.assertEqual(e, 1)
|
||||
self.assertEqual(s, 1)
|
||||
else:
|
||||
self.assertRaises(OSError, setresgid, 1, 1, 1)
|
||||
|
||||
class test_sort_uniq(unittest.TestCase):
|
||||
def test_sort_uniq(self):
|
||||
l = [ 'c', 'a', 'b', 'a' ]
|
||||
self.assertEqual(sort_uniq(l), [ 'a', 'b', 'c'])
|
||||
|
||||
class test_user_credentials(unittest.TestCase):
|
||||
def test_negatives(self):
|
||||
self.assertRaises(PrivilegeFail, user_credentials, -1, 0, [])
|
||||
self.assertRaises(PrivilegeFail, user_credentials, 0, -1, [])
|
||||
uc = user_credentials(0, 0, [0, 1])
|
||||
|
||||
class test_eql_sups(unittest.TestCase):
|
||||
def test_equal(self):
|
||||
self.assert_(eql_sups([0, 1, 2], [0, 1, 2]))
|
||||
def test_contains_egid(self):
|
||||
self.assert_(eql_sups(sort_uniq([0, 1, 2, os.getegid()]), [0, 1, 2]))
|
||||
self.assert_(eql_sups(sort_uniq([0, 1, os.getegid(), 9999]), [0, 1, 9999]))
|
||||
|
||||
class test_get_sups(unittest.TestCase):
|
||||
def test(self):
|
||||
self.assertEqual(os.getgroups(), get_sups())
|
||||
|
||||
class test_set_sups(unittest.TestCase):
|
||||
def test_equal(self):
|
||||
sups = os.getgroups()
|
||||
set_sups(sups)
|
||||
self.assertEqual(sups, os.getgroups())
|
||||
def test_unequal(self):
|
||||
old_sups = os.getgroups()
|
||||
sups = [ 1, 2, 3 ]
|
||||
if os.geteuid() == 0:
|
||||
set_sups(sups)
|
||||
self.assert_(eql_sups(os.getgroups(), sups))
|
||||
# Clean up by resetting supplementary groups
|
||||
set_sups(old_sups)
|
||||
else:
|
||||
self.assertRaises(OSError, set_sups, sups)
|
||||
|
||||
class test_res_ids(unittest.TestCase):
|
||||
def test_res_ids(self):
|
||||
ids = res_ids(1, 2, 3)
|
||||
self.assertEqual(ids.r, 1)
|
||||
self.assertEqual(ids.e, 2)
|
||||
self.assertEqual(ids.s, 3)
|
||||
|
||||
class test_proc_credentials(unittest.TestCase):
|
||||
def test_pc(self):
|
||||
pc = proc_credentials()
|
||||
self.assertEqual(pc.uids.r, os.getuid())
|
||||
self.assertEqual(pc.uids.e, os.geteuid())
|
||||
self.assertEqual(pc.uids.s, (getresuid())[2])
|
||||
self.assertEqual(pc.gids.r, os.getgid())
|
||||
self.assertEqual(pc.gids.e, os.getegid())
|
||||
self.assertEqual(pc.gids.s, (getresgid())[2])
|
||||
self.assertEqual(pc.sups, sort_uniq(os.getgroups()))
|
||||
|
||||
class test_get_fs_ids(unittest.TestCase):
|
||||
def test_get_fs_ids(self):
|
||||
uid, gid = get_fs_ids()
|
||||
self.assertEqual(uid, os.getuid())
|
||||
self.assertEqual(gid, os.getgid())
|
||||
|
||||
class test_drop_privs(unittest.TestCase):
|
||||
def test_drop_privs(self):
|
||||
"""This test must be run last"""
|
||||
if os.geteuid() == 0:
|
||||
drop_privileges_permanently(1, 1, [1])
|
||||
|
||||
suite = unittest.TestSuite()
|
||||
suite.addTest(unittest.makeSuite(test_getresXid))
|
||||
suite.addTest(unittest.makeSuite(test_setresuid))
|
||||
suite.addTest(unittest.makeSuite(test_setresgid))
|
||||
suite.addTest(unittest.makeSuite(test_sort_uniq))
|
||||
suite.addTest(unittest.makeSuite(test_user_credentials))
|
||||
suite.addTest(unittest.makeSuite(test_eql_sups))
|
||||
suite.addTest(unittest.makeSuite(test_set_sups))
|
||||
suite.addTest(unittest.makeSuite(test_res_ids))
|
||||
suite.addTest(unittest.makeSuite(test_proc_credentials))
|
||||
suite.addTest(unittest.makeSuite(test_get_fs_ids))
|
||||
suite.addTest(unittest.makeSuite(test_drop_privs))
|
||||
unittest.TextTestRunner().run(suite)
|
@ -3,30 +3,36 @@
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import _thread as thread
|
||||
from distutils.util import strtobool
|
||||
|
||||
import urllib.parse as urlparse
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import bottle
|
||||
from bottle import Bottle, static_file, view, request, HTTPResponse, redirect, abort
|
||||
from bottle import (
|
||||
Bottle,
|
||||
static_file,
|
||||
view,
|
||||
request,
|
||||
HTTPResponse,
|
||||
redirect,
|
||||
)
|
||||
|
||||
from beaker.middleware import SessionMiddleware
|
||||
|
||||
from zerobin import __version__
|
||||
from zerobin.utils import (
|
||||
settings,
|
||||
SettingsValidationError,
|
||||
ensure_var_env,
|
||||
ensure_app_context,
|
||||
check_password,
|
||||
settings,
|
||||
)
|
||||
from zerobin.paste import Paste
|
||||
|
||||
|
||||
ensure_var_env()
|
||||
ensure_app_context()
|
||||
|
||||
|
||||
GLOBAL_CONTEXT = {
|
||||
@ -37,15 +43,9 @@ GLOBAL_CONTEXT = {
|
||||
}
|
||||
|
||||
|
||||
app = SessionMiddleware(
|
||||
Bottle(),
|
||||
{
|
||||
"session.type": "file",
|
||||
"session.cookie_expires": 300,
|
||||
"session.data_dir": settings.SESSIONS_DIR,
|
||||
"session.auto": True,
|
||||
},
|
||||
)
|
||||
app = Bottle()
|
||||
|
||||
ADMIN_LOGIN_URL = settings.ADMIN_URL + "login/"
|
||||
|
||||
|
||||
@app.route("/")
|
||||
@ -54,111 +54,111 @@ def index():
|
||||
return GLOBAL_CONTEXT
|
||||
|
||||
|
||||
@app.route("/faq/")
|
||||
@app.get("/faq/")
|
||||
@view("faq")
|
||||
def faq():
|
||||
return GLOBAL_CONTEXT
|
||||
|
||||
@app.route("/buy_bitcoin")
|
||||
@view("buy_bitcoin")
|
||||
def index():
|
||||
return GLOBAL_CONTEXT
|
||||
|
||||
@app.route(settings.ADMIN_URL, method="GET")
|
||||
|
||||
@app.get(settings.ADMIN_URL)
|
||||
@app.post(settings.ADMIN_URL)
|
||||
@view("admin")
|
||||
def admin():
|
||||
session = request.environ.get("beaker.session")
|
||||
if not session or not session.get("is_authenticated"):
|
||||
redirect(settings.ADMIN_URL + "/login")
|
||||
redirect(ADMIN_LOGIN_URL)
|
||||
|
||||
return {}
|
||||
paste_id = request.forms.get("paste", "")
|
||||
if paste_id:
|
||||
try:
|
||||
if "/paste/" in paste_id:
|
||||
paste_id = urlparse(paste_id).path.split("/paste/")[-1]
|
||||
paste = Paste.load(paste_id)
|
||||
paste.delete()
|
||||
except (TypeError, ValueError, FileNotFoundError):
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Cannot find paste '{paste_id}'",
|
||||
**GLOBAL_CONTEXT,
|
||||
}
|
||||
|
||||
return {"status": "ok", "message": "Paste deleted", **GLOBAL_CONTEXT}
|
||||
|
||||
return {"status": "ok", "message": "", **GLOBAL_CONTEXT}
|
||||
|
||||
|
||||
@app.route(settings.ADMIN_URL + "/:paste_id", method="DELETE")
|
||||
@view("admin")
|
||||
def delete_paste_from_admin(paste_id):
|
||||
session = request.environ.get("beaker.session")
|
||||
if not session or not session.get("is_authenticated"):
|
||||
abort(403, "Sorry, access denied.")
|
||||
|
||||
try:
|
||||
paste = Paste.load(paste_id)
|
||||
except (TypeError, ValueError):
|
||||
return error404(ValueError)
|
||||
|
||||
paste.delete()
|
||||
|
||||
return {"status": "ok", "message": "Paste deleted"}
|
||||
|
||||
|
||||
@app.route(settings.ADMIN_URL + "/login")
|
||||
@app.get(ADMIN_LOGIN_URL)
|
||||
@app.post(ADMIN_LOGIN_URL)
|
||||
@view("login")
|
||||
def login():
|
||||
|
||||
password = request.forms.get("password")
|
||||
if password:
|
||||
if not check_password(password):
|
||||
return {"error": "Wrong password"}
|
||||
return {"status": "error", "message": "Wrong password", **GLOBAL_CONTEXT}
|
||||
|
||||
session = request.environ.get("beaker.session")
|
||||
session["is_authenticated"] = True
|
||||
session.save()
|
||||
|
||||
redirect(settings.ADMIN_URL)
|
||||
else:
|
||||
return {}
|
||||
|
||||
return {"status": "ok", **GLOBAL_CONTEXT}
|
||||
|
||||
|
||||
@app.route("/paste/create", method="POST")
|
||||
@app.post(settings.ADMIN_URL + "logout/")
|
||||
@view("logout")
|
||||
def logout():
|
||||
session = request.environ.get("beaker.session")
|
||||
session["is_authenticated"] = False
|
||||
session.save()
|
||||
redirect("/")
|
||||
|
||||
|
||||
@app.post("/paste/create")
|
||||
def create_paste():
|
||||
|
||||
try:
|
||||
body = urlparse.parse_qs(request.body.read(int(settings.MAX_SIZE * 1.1)))
|
||||
except ValueError:
|
||||
# Reject what is too small, too big, or what does not seem encrypted to
|
||||
# limit a abuses
|
||||
content = request.forms.get("content", "")
|
||||
if '{"iv":' not in content or not (0 < len(content) < settings.MAX_SIZE):
|
||||
return {"status": "error", "message": "Wrong data payload."}
|
||||
|
||||
try:
|
||||
content = "".join(x.decode("utf8") for x in body[b"content"])
|
||||
except (UnicodeDecodeError, KeyError):
|
||||
return {
|
||||
"status": "error",
|
||||
"message": "Encoding error: the paste couldn't be saved.",
|
||||
}
|
||||
expiration = request.forms.get("expiration", "burn_after_reading")
|
||||
title = request.forms.get("title", "")
|
||||
btc_tip_address = request.forms.get("btcTipAddress", "")
|
||||
|
||||
if '{"iv":' not in content: # reject silently non encrypted content
|
||||
return {"status": "error", "message": "Wrong data payload."}
|
||||
paste = Paste(
|
||||
expiration=expiration,
|
||||
content=content,
|
||||
uuid_length=settings.PASTE_ID_LENGTH,
|
||||
title=title,
|
||||
btc_tip_address=btc_tip_address,
|
||||
)
|
||||
paste.save()
|
||||
|
||||
# check size of the paste. if more than settings return error
|
||||
# without saving paste. prevent from unusual use of the
|
||||
# system. need to be improved
|
||||
if 0 < len(content) < settings.MAX_SIZE:
|
||||
expiration = body.get(b"expiration", [b"burn_after_reading"])[0]
|
||||
paste = Paste(
|
||||
expiration=expiration.decode("utf8"),
|
||||
content=content,
|
||||
uuid_length=settings.PASTE_ID_LENGTH,
|
||||
# If refresh time elapsed pick up, update the counter
|
||||
if settings.DISPLAY_COUNTER:
|
||||
|
||||
paste.increment_counter()
|
||||
|
||||
now = datetime.now()
|
||||
timeout = GLOBAL_CONTEXT["refresh_counter"] + timedelta(
|
||||
seconds=settings.REFRESH_COUNTER
|
||||
)
|
||||
paste.save()
|
||||
if timeout < now:
|
||||
GLOBAL_CONTEXT["pastes_count"] = Paste.get_pastes_count()
|
||||
GLOBAL_CONTEXT["refresh_counter"] = now
|
||||
|
||||
# display counter
|
||||
if settings.DISPLAY_COUNTER:
|
||||
|
||||
# increment paste counter
|
||||
paste.increment_counter()
|
||||
|
||||
# if refresh time elapsed pick up new counter value
|
||||
now = datetime.now()
|
||||
timeout = GLOBAL_CONTEXT["refresh_counter"] + timedelta(
|
||||
seconds=settings.REFRESH_COUNTER
|
||||
)
|
||||
if timeout < now:
|
||||
GLOBAL_CONTEXT["pastes_count"] = Paste.get_pastes_count()
|
||||
GLOBAL_CONTEXT["refresh_counter"] = now
|
||||
|
||||
return {"status": "ok", "paste": paste.uuid, "owner_key": paste.owner_key}
|
||||
|
||||
return {
|
||||
"status": "error",
|
||||
"message": "Serveur error: the paste couldn't be saved. " "Please try later.",
|
||||
}
|
||||
return {"status": "ok", "paste": paste.uuid, "owner_key": paste.owner_key}
|
||||
|
||||
|
||||
@app.route("/paste/:paste_id", method="GET")
|
||||
@app.get("/paste/:paste_id")
|
||||
@view("paste")
|
||||
def display_paste(paste_id):
|
||||
|
||||
@ -191,7 +191,7 @@ def display_paste(paste_id):
|
||||
return {"paste": paste, "keep_alive": keep_alive, **GLOBAL_CONTEXT}
|
||||
|
||||
|
||||
@app.route("/paste/:paste_id", method="DELETE")
|
||||
@app.delete("/paste/:paste_id")
|
||||
def delete_paste(paste_id):
|
||||
|
||||
try:
|
||||
@ -216,36 +216,52 @@ def error404(code):
|
||||
return GLOBAL_CONTEXT
|
||||
|
||||
|
||||
@app.route("/static/<filename:path>")
|
||||
@app.get("/static/<filename:path>")
|
||||
def server_static(filename):
|
||||
return static_file(filename, root=settings.STATIC_FILES_ROOT)
|
||||
|
||||
|
||||
def get_app(debug=None, settings_file="", compressed_static=None, settings=settings):
|
||||
def get_app(debug=None, config_dir="", data_dir=""):
|
||||
"""
|
||||
Return a tuple (settings, app) configured using passed
|
||||
parameters and/or a setting file.
|
||||
"""
|
||||
|
||||
settings_file = settings_file or os.environ.get("ZEROBIN_SETTINGS_FILE")
|
||||
data_dir = data_dir or os.environ.get("ZEROBIN_DATA_DIR")
|
||||
config_dir = config_dir or os.environ.get("ZEROBIN_CONFIG_DIR")
|
||||
|
||||
if settings_file:
|
||||
settings.update_with_file(os.path.realpath(settings_file))
|
||||
ensure_app_context(config_dir=config_dir, data_dir=data_dir)
|
||||
|
||||
if debug is None:
|
||||
settings.DEBUG = bool(
|
||||
strtobool(os.environ.get("ZEROBIN_DEBUG", str(settings.DEBUG)))
|
||||
)
|
||||
else:
|
||||
settings.DEBUG = debug
|
||||
|
||||
settings.DISPLAY_COUNTER = bool(
|
||||
os.environ.get("ZEROBIN_DISPLAY_COUNTER", settings.DISPLAY_COUNTER)
|
||||
)
|
||||
settings.REFRESH_COUNTER = int(
|
||||
os.environ.get("ZEROBIN_REFRESH_COUNTER", settings.REFRESH_COUNTER)
|
||||
)
|
||||
settings.MAX_SIZE = int(os.environ.get("ZEROBIN_MAX_SIZE", settings.MAX_SIZE))
|
||||
settings.PASTE_ID_LENGTH = int(
|
||||
os.environ.get("ZEROBIN_PASTE_ID_LENGTH", settings.PASTE_ID_LENGTH)
|
||||
)
|
||||
|
||||
if settings.PASTE_ID_LENGTH < 4:
|
||||
raise SettingsValidationError("PASTE_ID_LENGTH cannot be lower than 4")
|
||||
|
||||
if compressed_static is not None:
|
||||
settings.COMPRESSED_STATIC_FILES = compressed_static
|
||||
|
||||
if debug is not None:
|
||||
settings.DEBUG = debug
|
||||
|
||||
# make sure the templates can be loaded
|
||||
for d in reversed(settings.TEMPLATE_DIRS):
|
||||
bottle.TEMPLATE_PATH.insert(0, d)
|
||||
|
||||
if settings.DEBUG:
|
||||
bottle.debug(True)
|
||||
|
||||
return settings, app
|
||||
|
||||
|
||||
app = SessionMiddleware(
|
||||
app,
|
||||
{
|
||||
"session.type": "file",
|
||||
"session.cookie_expires": 300,
|
||||
"session.data_dir": settings.SESSIONS_DIR,
|
||||
"session.auto": True,
|
||||
},
|
||||
)
|
||||
|
686
zerobin/static/css/bootstrap-responsive.css
vendored
@ -1,686 +0,0 @@
|
||||
/*!
|
||||
* Bootstrap Responsive v2.0.2
|
||||
*
|
||||
* Copyright 2012 Twitter, Inc
|
||||
* Licensed under the Apache License v2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Designed and built with all the love in the world @twitter by @mdo and @fat.
|
||||
*/
|
||||
.clearfix {
|
||||
*zoom: 1;
|
||||
}
|
||||
.clearfix:before,
|
||||
.clearfix:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.clearfix:after {
|
||||
clear: both;
|
||||
}
|
||||
.hide-text {
|
||||
overflow: hidden;
|
||||
text-indent: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.input-block-level {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: 28px;
|
||||
/* Make inputs at least the height of their button counterpart */
|
||||
|
||||
/* Makes inputs behave like true block-level elements */
|
||||
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
.visible-phone {
|
||||
display: none;
|
||||
}
|
||||
.visible-tablet {
|
||||
display: none;
|
||||
}
|
||||
.visible-desktop {
|
||||
display: block;
|
||||
}
|
||||
.hidden-phone {
|
||||
display: block;
|
||||
}
|
||||
.hidden-tablet {
|
||||
display: block;
|
||||
}
|
||||
.hidden-desktop {
|
||||
display: none;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.visible-phone {
|
||||
display: block;
|
||||
}
|
||||
.hidden-phone {
|
||||
display: none;
|
||||
}
|
||||
.hidden-desktop {
|
||||
display: block;
|
||||
}
|
||||
.visible-desktop {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) and (max-width: 979px) {
|
||||
.visible-tablet {
|
||||
display: block;
|
||||
}
|
||||
.hidden-tablet {
|
||||
display: none;
|
||||
}
|
||||
.hidden-desktop {
|
||||
display: block;
|
||||
}
|
||||
.visible-desktop {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.nav-collapse {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
}
|
||||
.page-header h1 small {
|
||||
display: block;
|
||||
line-height: 18px;
|
||||
}
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.form-horizontal .control-group > label {
|
||||
float: none;
|
||||
width: auto;
|
||||
padding-top: 0;
|
||||
text-align: left;
|
||||
}
|
||||
.form-horizontal .controls {
|
||||
margin-left: 0;
|
||||
}
|
||||
.form-horizontal .control-list {
|
||||
padding-top: 0;
|
||||
}
|
||||
.form-horizontal .form-actions {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.modal {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
}
|
||||
.modal.fade.in {
|
||||
top: auto;
|
||||
}
|
||||
.modal-header .close {
|
||||
padding: 10px;
|
||||
margin: -10px;
|
||||
}
|
||||
.carousel-caption {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
body {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
.navbar-fixed-top {
|
||||
margin-left: -20px;
|
||||
margin-right: -20px;
|
||||
}
|
||||
.container {
|
||||
width: auto;
|
||||
}
|
||||
.row-fluid {
|
||||
width: 100%;
|
||||
}
|
||||
.row {
|
||||
margin-left: 0;
|
||||
}
|
||||
.row > [class*="span"],
|
||||
.row-fluid > [class*="span"] {
|
||||
float: none;
|
||||
display: block;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
}
|
||||
.thumbnails [class*="span"] {
|
||||
width: auto;
|
||||
}
|
||||
input[class*="span"],
|
||||
select[class*="span"],
|
||||
textarea[class*="span"],
|
||||
.uneditable-input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: 28px;
|
||||
/* Make inputs at least the height of their button counterpart */
|
||||
|
||||
/* Makes inputs behave like true block-level elements */
|
||||
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.input-prepend input[class*="span"],
|
||||
.input-append input[class*="span"] {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) and (max-width: 979px) {
|
||||
.row {
|
||||
margin-left: -20px;
|
||||
*zoom: 1;
|
||||
}
|
||||
.row:before,
|
||||
.row:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.row:after {
|
||||
clear: both;
|
||||
}
|
||||
[class*="span"] {
|
||||
float: left;
|
||||
margin-left: 20px;
|
||||
}
|
||||
.container,
|
||||
.navbar-fixed-top .container,
|
||||
.navbar-fixed-bottom .container {
|
||||
width: 724px;
|
||||
}
|
||||
.span12 {
|
||||
width: 724px;
|
||||
}
|
||||
.span11 {
|
||||
width: 662px;
|
||||
}
|
||||
.span10 {
|
||||
width: 600px;
|
||||
}
|
||||
.span9 {
|
||||
width: 538px;
|
||||
}
|
||||
.span8 {
|
||||
width: 476px;
|
||||
}
|
||||
.span7 {
|
||||
width: 414px;
|
||||
}
|
||||
.span6 {
|
||||
width: 352px;
|
||||
}
|
||||
.span5 {
|
||||
width: 290px;
|
||||
}
|
||||
.span4 {
|
||||
width: 228px;
|
||||
}
|
||||
.span3 {
|
||||
width: 166px;
|
||||
}
|
||||
.span2 {
|
||||
width: 104px;
|
||||
}
|
||||
.span1 {
|
||||
width: 42px;
|
||||
}
|
||||
.offset12 {
|
||||
margin-left: 764px;
|
||||
}
|
||||
.offset11 {
|
||||
margin-left: 702px;
|
||||
}
|
||||
.offset10 {
|
||||
margin-left: 640px;
|
||||
}
|
||||
.offset9 {
|
||||
margin-left: 578px;
|
||||
}
|
||||
.offset8 {
|
||||
margin-left: 516px;
|
||||
}
|
||||
.offset7 {
|
||||
margin-left: 454px;
|
||||
}
|
||||
.offset6 {
|
||||
margin-left: 392px;
|
||||
}
|
||||
.offset5 {
|
||||
margin-left: 330px;
|
||||
}
|
||||
.offset4 {
|
||||
margin-left: 268px;
|
||||
}
|
||||
.offset3 {
|
||||
margin-left: 206px;
|
||||
}
|
||||
.offset2 {
|
||||
margin-left: 144px;
|
||||
}
|
||||
.offset1 {
|
||||
margin-left: 82px;
|
||||
}
|
||||
.row-fluid {
|
||||
width: 100%;
|
||||
*zoom: 1;
|
||||
}
|
||||
.row-fluid:before,
|
||||
.row-fluid:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.row-fluid:after {
|
||||
clear: both;
|
||||
}
|
||||
.row-fluid > [class*="span"] {
|
||||
float: left;
|
||||
margin-left: 2.762430939%;
|
||||
}
|
||||
.row-fluid > [class*="span"]:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
.row-fluid > .span12 {
|
||||
width: 99.999999993%;
|
||||
}
|
||||
.row-fluid > .span11 {
|
||||
width: 91.436464082%;
|
||||
}
|
||||
.row-fluid > .span10 {
|
||||
width: 82.87292817100001%;
|
||||
}
|
||||
.row-fluid > .span9 {
|
||||
width: 74.30939226%;
|
||||
}
|
||||
.row-fluid > .span8 {
|
||||
width: 65.74585634900001%;
|
||||
}
|
||||
.row-fluid > .span7 {
|
||||
width: 57.182320438000005%;
|
||||
}
|
||||
.row-fluid > .span6 {
|
||||
width: 48.618784527%;
|
||||
}
|
||||
.row-fluid > .span5 {
|
||||
width: 40.055248616%;
|
||||
}
|
||||
.row-fluid > .span4 {
|
||||
width: 31.491712705%;
|
||||
}
|
||||
.row-fluid > .span3 {
|
||||
width: 22.928176794%;
|
||||
}
|
||||
.row-fluid > .span2 {
|
||||
width: 14.364640883%;
|
||||
}
|
||||
.row-fluid > .span1 {
|
||||
width: 5.801104972%;
|
||||
}
|
||||
input,
|
||||
textarea,
|
||||
.uneditable-input {
|
||||
margin-left: 0;
|
||||
}
|
||||
input.span12, textarea.span12, .uneditable-input.span12 {
|
||||
width: 714px;
|
||||
}
|
||||
input.span11, textarea.span11, .uneditable-input.span11 {
|
||||
width: 652px;
|
||||
}
|
||||
input.span10, textarea.span10, .uneditable-input.span10 {
|
||||
width: 590px;
|
||||
}
|
||||
input.span9, textarea.span9, .uneditable-input.span9 {
|
||||
width: 528px;
|
||||
}
|
||||
input.span8, textarea.span8, .uneditable-input.span8 {
|
||||
width: 466px;
|
||||
}
|
||||
input.span7, textarea.span7, .uneditable-input.span7 {
|
||||
width: 404px;
|
||||
}
|
||||
input.span6, textarea.span6, .uneditable-input.span6 {
|
||||
width: 342px;
|
||||
}
|
||||
input.span5, textarea.span5, .uneditable-input.span5 {
|
||||
width: 280px;
|
||||
}
|
||||
input.span4, textarea.span4, .uneditable-input.span4 {
|
||||
width: 218px;
|
||||
}
|
||||
input.span3, textarea.span3, .uneditable-input.span3 {
|
||||
width: 156px;
|
||||
}
|
||||
input.span2, textarea.span2, .uneditable-input.span2 {
|
||||
width: 94px;
|
||||
}
|
||||
input.span1, textarea.span1, .uneditable-input.span1 {
|
||||
width: 32px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 979px) {
|
||||
body {
|
||||
padding-top: 0;
|
||||
}
|
||||
.navbar-fixed-top {
|
||||
position: static;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.navbar-fixed-top .navbar-inner {
|
||||
padding: 5px;
|
||||
}
|
||||
.navbar .container {
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
.navbar .brand {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
margin: 0 0 0 -5px;
|
||||
}
|
||||
.navbar .nav-collapse {
|
||||
clear: left;
|
||||
}
|
||||
.navbar .nav {
|
||||
float: none;
|
||||
margin: 0 0 9px;
|
||||
}
|
||||
.navbar .nav > li {
|
||||
float: none;
|
||||
}
|
||||
.navbar .nav > li > a {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.navbar .nav > .divider-vertical {
|
||||
display: none;
|
||||
}
|
||||
.navbar .nav .nav-header {
|
||||
color: #999999;
|
||||
text-shadow: none;
|
||||
}
|
||||
.navbar .nav > li > a,
|
||||
.navbar .dropdown-menu a {
|
||||
padding: 6px 15px;
|
||||
font-weight: bold;
|
||||
color: #999999;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.navbar .dropdown-menu li + li a {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.navbar .nav > li > a:hover,
|
||||
.navbar .dropdown-menu a:hover {
|
||||
background-color: #222222;
|
||||
}
|
||||
.navbar .dropdown-menu {
|
||||
position: static;
|
||||
top: auto;
|
||||
left: auto;
|
||||
float: none;
|
||||
display: block;
|
||||
max-width: none;
|
||||
margin: 0 15px;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.navbar .dropdown-menu:before,
|
||||
.navbar .dropdown-menu:after {
|
||||
display: none;
|
||||
}
|
||||
.navbar .dropdown-menu .divider {
|
||||
display: none;
|
||||
}
|
||||
.navbar-form,
|
||||
.navbar-search {
|
||||
float: none;
|
||||
padding: 9px 15px;
|
||||
margin: 9px 0;
|
||||
border-top: 1px solid #222222;
|
||||
border-bottom: 1px solid #222222;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.navbar .nav.pull-right {
|
||||
float: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
.navbar-static .navbar-inner {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.btn-navbar {
|
||||
display: block;
|
||||
}
|
||||
.nav-collapse {
|
||||
overflow: hidden;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
@media (min-width: 980px) {
|
||||
.nav-collapse.collapse {
|
||||
height: auto !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
.row {
|
||||
margin-left: -30px;
|
||||
*zoom: 1;
|
||||
}
|
||||
.row:before,
|
||||
.row:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.row:after {
|
||||
clear: both;
|
||||
}
|
||||
[class*="span"] {
|
||||
float: left;
|
||||
margin-left: 30px;
|
||||
}
|
||||
.container,
|
||||
.navbar-fixed-top .container,
|
||||
.navbar-fixed-bottom .container {
|
||||
width: 1170px;
|
||||
}
|
||||
.span12 {
|
||||
width: 1170px;
|
||||
}
|
||||
.span11 {
|
||||
width: 1070px;
|
||||
}
|
||||
.span10 {
|
||||
width: 970px;
|
||||
}
|
||||
.span9 {
|
||||
width: 870px;
|
||||
}
|
||||
.span8 {
|
||||
width: 770px;
|
||||
}
|
||||
.span7 {
|
||||
width: 670px;
|
||||
}
|
||||
.span6 {
|
||||
width: 570px;
|
||||
}
|
||||
.span5 {
|
||||
width: 470px;
|
||||
}
|
||||
.span4 {
|
||||
width: 370px;
|
||||
}
|
||||
.span3 {
|
||||
width: 270px;
|
||||
}
|
||||
.span2 {
|
||||
width: 170px;
|
||||
}
|
||||
.span1 {
|
||||
width: 70px;
|
||||
}
|
||||
.offset12 {
|
||||
margin-left: 1230px;
|
||||
}
|
||||
.offset11 {
|
||||
margin-left: 1130px;
|
||||
}
|
||||
.offset10 {
|
||||
margin-left: 1030px;
|
||||
}
|
||||
.offset9 {
|
||||
margin-left: 930px;
|
||||
}
|
||||
.offset8 {
|
||||
margin-left: 830px;
|
||||
}
|
||||
.offset7 {
|
||||
margin-left: 730px;
|
||||
}
|
||||
.offset6 {
|
||||
margin-left: 630px;
|
||||
}
|
||||
.offset5 {
|
||||
margin-left: 530px;
|
||||
}
|
||||
.offset4 {
|
||||
margin-left: 430px;
|
||||
}
|
||||
.offset3 {
|
||||
margin-left: 330px;
|
||||
}
|
||||
.offset2 {
|
||||
margin-left: 230px;
|
||||
}
|
||||
.offset1 {
|
||||
margin-left: 130px;
|
||||
}
|
||||
.row-fluid {
|
||||
width: 100%;
|
||||
*zoom: 1;
|
||||
}
|
||||
.row-fluid:before,
|
||||
.row-fluid:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.row-fluid:after {
|
||||
clear: both;
|
||||
}
|
||||
.row-fluid > [class*="span"] {
|
||||
float: left;
|
||||
margin-left: 2.564102564%;
|
||||
}
|
||||
.row-fluid > [class*="span"]:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
.row-fluid > .span12 {
|
||||
width: 100%;
|
||||
}
|
||||
.row-fluid > .span11 {
|
||||
width: 91.45299145300001%;
|
||||
}
|
||||
.row-fluid > .span10 {
|
||||
width: 82.905982906%;
|
||||
}
|
||||
.row-fluid > .span9 {
|
||||
width: 74.358974359%;
|
||||
}
|
||||
.row-fluid > .span8 {
|
||||
width: 65.81196581200001%;
|
||||
}
|
||||
.row-fluid > .span7 {
|
||||
width: 57.264957265%;
|
||||
}
|
||||
.row-fluid > .span6 {
|
||||
width: 48.717948718%;
|
||||
}
|
||||
.row-fluid > .span5 {
|
||||
width: 40.170940171000005%;
|
||||
}
|
||||
.row-fluid > .span4 {
|
||||
width: 31.623931624%;
|
||||
}
|
||||
.row-fluid > .span3 {
|
||||
width: 23.076923077%;
|
||||
}
|
||||
.row-fluid > .span2 {
|
||||
width: 14.529914530000001%;
|
||||
}
|
||||
.row-fluid > .span1 {
|
||||
width: 5.982905983%;
|
||||
}
|
||||
input,
|
||||
textarea,
|
||||
.uneditable-input {
|
||||
margin-left: 0;
|
||||
}
|
||||
input.span12, textarea.span12, .uneditable-input.span12 {
|
||||
width: 1160px;
|
||||
}
|
||||
input.span11, textarea.span11, .uneditable-input.span11 {
|
||||
width: 1060px;
|
||||
}
|
||||
input.span10, textarea.span10, .uneditable-input.span10 {
|
||||
width: 960px;
|
||||
}
|
||||
input.span9, textarea.span9, .uneditable-input.span9 {
|
||||
width: 860px;
|
||||
}
|
||||
input.span8, textarea.span8, .uneditable-input.span8 {
|
||||
width: 760px;
|
||||
}
|
||||
input.span7, textarea.span7, .uneditable-input.span7 {
|
||||
width: 660px;
|
||||
}
|
||||
input.span6, textarea.span6, .uneditable-input.span6 {
|
||||
width: 560px;
|
||||
}
|
||||
input.span5, textarea.span5, .uneditable-input.span5 {
|
||||
width: 460px;
|
||||
}
|
||||
input.span4, textarea.span4, .uneditable-input.span4 {
|
||||
width: 360px;
|
||||
}
|
||||
input.span3, textarea.span3, .uneditable-input.span3 {
|
||||
width: 260px;
|
||||
}
|
||||
input.span2, textarea.span2, .uneditable-input.span2 {
|
||||
width: 160px;
|
||||
}
|
||||
input.span1, textarea.span1, .uneditable-input.span1 {
|
||||
width: 60px;
|
||||
}
|
||||
.thumbnails {
|
||||
margin-left: -30px;
|
||||
}
|
||||
.thumbnails > li {
|
||||
margin-left: 30px;
|
||||
}
|
||||
}
|
12
zerobin/static/css/bootstrap-responsive.min.css
vendored
3990
zerobin/static/css/bootstrap.css
vendored
689
zerobin/static/css/bootstrap.min.css
vendored
@ -1,689 +0,0 @@
|
||||
article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
|
||||
audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
|
||||
audio:not([controls]){display:none;}
|
||||
html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
|
||||
a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
|
||||
a:hover,a:active{outline:0;}
|
||||
sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
|
||||
sup{top:-0.5em;}
|
||||
sub{bottom:-0.25em;}
|
||||
img{height:auto;border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;}
|
||||
button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
|
||||
button,input{*overflow:visible;line-height:normal;}
|
||||
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
|
||||
button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
|
||||
input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
|
||||
input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
|
||||
textarea{overflow:auto;vertical-align:top;}
|
||||
.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";}
|
||||
.clearfix:after{clear:both;}
|
||||
.hide-text{overflow:hidden;text-indent:100%;white-space:nowrap;}
|
||||
.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}
|
||||
body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;}
|
||||
a{color:#0088cc;text-decoration:none;}
|
||||
a:hover{color:#005580;text-decoration:underline;}
|
||||
.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";}
|
||||
.row:after{clear:both;}
|
||||
[class*="span"]{float:left;margin-left:20px;}
|
||||
.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
|
||||
.span12{width:940px;}
|
||||
.span11{width:860px;}
|
||||
.span10{width:780px;}
|
||||
.span9{width:700px;}
|
||||
.span8{width:620px;}
|
||||
.span7{width:540px;}
|
||||
.span6{width:460px;}
|
||||
.span5{width:380px;}
|
||||
.span4{width:300px;}
|
||||
.span3{width:220px;}
|
||||
.span2{width:140px;}
|
||||
.span1{width:60px;}
|
||||
.offset12{margin-left:980px;}
|
||||
.offset11{margin-left:900px;}
|
||||
.offset10{margin-left:820px;}
|
||||
.offset9{margin-left:740px;}
|
||||
.offset8{margin-left:660px;}
|
||||
.offset7{margin-left:580px;}
|
||||
.offset6{margin-left:500px;}
|
||||
.offset5{margin-left:420px;}
|
||||
.offset4{margin-left:340px;}
|
||||
.offset3{margin-left:260px;}
|
||||
.offset2{margin-left:180px;}
|
||||
.offset1{margin-left:100px;}
|
||||
.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";}
|
||||
.row-fluid:after{clear:both;}
|
||||
.row-fluid>[class*="span"]{float:left;margin-left:2.127659574%;}
|
||||
.row-fluid>[class*="span"]:first-child{margin-left:0;}
|
||||
.row-fluid > .span12{width:99.99999998999999%;}
|
||||
.row-fluid > .span11{width:91.489361693%;}
|
||||
.row-fluid > .span10{width:82.97872339599999%;}
|
||||
.row-fluid > .span9{width:74.468085099%;}
|
||||
.row-fluid > .span8{width:65.95744680199999%;}
|
||||
.row-fluid > .span7{width:57.446808505%;}
|
||||
.row-fluid > .span6{width:48.93617020799999%;}
|
||||
.row-fluid > .span5{width:40.425531911%;}
|
||||
.row-fluid > .span4{width:31.914893614%;}
|
||||
.row-fluid > .span3{width:23.404255317%;}
|
||||
.row-fluid > .span2{width:14.89361702%;}
|
||||
.row-fluid > .span1{width:6.382978723%;}
|
||||
.container{margin-left:auto;margin-right:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";}
|
||||
.container:after{clear:both;}
|
||||
.container-fluid{padding-left:20px;padding-right:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";}
|
||||
.container-fluid:after{clear:both;}
|
||||
p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p small{font-size:11px;color:#999999;}
|
||||
.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;}
|
||||
h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;}
|
||||
h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;}
|
||||
h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;}
|
||||
h3{line-height:27px;font-size:18px;}h3 small{font-size:14px;}
|
||||
h4,h5,h6{line-height:18px;}
|
||||
h4{font-size:14px;}h4 small{font-size:12px;}
|
||||
h5{font-size:12px;}
|
||||
h6{font-size:11px;color:#999999;text-transform:uppercase;}
|
||||
.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;}
|
||||
.page-header h1{line-height:1;}
|
||||
ul,ol{padding:0;margin:0 0 9px 25px;}
|
||||
ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
|
||||
ul{list-style:disc;}
|
||||
ol{list-style:decimal;}
|
||||
li{line-height:18px;}
|
||||
ul.unstyled,ol.unstyled{margin-left:0;list-style:none;}
|
||||
dl{margin-bottom:18px;}
|
||||
dt,dd{line-height:18px;}
|
||||
dt{font-weight:bold;line-height:17px;}
|
||||
dd{margin-left:9px;}
|
||||
.dl-horizontal dt{float:left;clear:left;width:120px;text-align:right;}
|
||||
.dl-horizontal dd{margin-left:130px;}
|
||||
hr{margin:18px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;}
|
||||
strong{font-weight:bold;}
|
||||
em{font-style:italic;}
|
||||
.muted{color:#999999;}
|
||||
abbr[title]{border-bottom:1px dotted #ddd;cursor:help;}
|
||||
abbr.initialism{font-size:90%;text-transform:uppercase;}
|
||||
blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;}
|
||||
blockquote small{display:block;line-height:18px;color:#999999;}blockquote small:before{content:'\2014 \00A0';}
|
||||
blockquote.pull-right{float:right;padding-left:0;padding-right:15px;border-left:0;border-right:5px solid #eeeeee;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;}
|
||||
q:before,q:after,blockquote:before,blockquote:after{content:"";}
|
||||
address{display:block;margin-bottom:18px;line-height:18px;font-style:normal;}
|
||||
small{font-size:100%;}
|
||||
cite{font-style:normal;}
|
||||
code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
|
||||
code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;}
|
||||
pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12.025px;line-height:18px;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;white-space:pre;white-space:pre-wrap;word-break:break-all;word-wrap:break-word;}pre.prettyprint{margin-bottom:18px;}
|
||||
pre code{padding:0;color:inherit;background-color:transparent;border:0;}
|
||||
.pre-scrollable{max-height:340px;overflow-y:scroll;}
|
||||
form{margin:0 0 18px;}
|
||||
fieldset{padding:0;margin:0;border:0;}
|
||||
legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333333;border:0;border-bottom:1px solid #eee;}legend small{font-size:13.5px;color:#999999;}
|
||||
label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:18px;}
|
||||
input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}
|
||||
label{display:block;margin-bottom:5px;color:#333333;}
|
||||
input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555555;border:1px solid #cccccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
|
||||
.uneditable-textarea{width:auto;height:auto;}
|
||||
label input,label textarea,label select{display:block;}
|
||||
input[type="image"],input[type="checkbox"],input[type="radio"]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border:0 \9;}
|
||||
input[type="image"]{border:0;}
|
||||
input[type="file"]{width:auto;padding:initial;line-height:initial;border:initial;background-color:#ffffff;background-color:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
|
||||
input[type="button"],input[type="reset"],input[type="submit"]{width:auto;height:auto;}
|
||||
select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px;}
|
||||
input[type="file"]{line-height:18px \9;}
|
||||
select{width:220px;background-color:#ffffff;}
|
||||
select[multiple],select[size]{height:auto;}
|
||||
input[type="image"]{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
|
||||
textarea{height:auto;}
|
||||
input[type="hidden"]{display:none;}
|
||||
.radio,.checkbox{padding-left:18px;}
|
||||
.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;}
|
||||
.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;}
|
||||
.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;}
|
||||
.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;}
|
||||
input,textarea{-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;}
|
||||
input:focus,textarea:focus{border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);outline:0;outline:thin dotted \9;}
|
||||
input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
|
||||
.input-mini{width:60px;}
|
||||
.input-small{width:90px;}
|
||||
.input-medium{width:150px;}
|
||||
.input-large{width:210px;}
|
||||
.input-xlarge{width:270px;}
|
||||
.input-xxlarge{width:530px;}
|
||||
input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{float:none;margin-left:0;}
|
||||
input,textarea,.uneditable-input{margin-left:0;}
|
||||
input.span12, textarea.span12, .uneditable-input.span12{width:930px;}
|
||||
input.span11, textarea.span11, .uneditable-input.span11{width:850px;}
|
||||
input.span10, textarea.span10, .uneditable-input.span10{width:770px;}
|
||||
input.span9, textarea.span9, .uneditable-input.span9{width:690px;}
|
||||
input.span8, textarea.span8, .uneditable-input.span8{width:610px;}
|
||||
input.span7, textarea.span7, .uneditable-input.span7{width:530px;}
|
||||
input.span6, textarea.span6, .uneditable-input.span6{width:450px;}
|
||||
input.span5, textarea.span5, .uneditable-input.span5{width:370px;}
|
||||
input.span4, textarea.span4, .uneditable-input.span4{width:290px;}
|
||||
input.span3, textarea.span3, .uneditable-input.span3{width:210px;}
|
||||
input.span2, textarea.span2, .uneditable-input.span2{width:130px;}
|
||||
input.span1, textarea.span1, .uneditable-input.span1{width:50px;}
|
||||
input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{background-color:#eeeeee;border-color:#ddd;cursor:not-allowed;}
|
||||
.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;}
|
||||
.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853;}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e;}
|
||||
.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;}
|
||||
.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;}
|
||||
.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48;}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392;}
|
||||
.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;}
|
||||
.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;}
|
||||
.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847;}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b;}
|
||||
.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;}
|
||||
input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;}
|
||||
.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#eeeeee;border-top:1px solid #ddd;*zoom:1;}.form-actions:before,.form-actions:after{display:table;content:"";}
|
||||
.form-actions:after{clear:both;}
|
||||
.uneditable-input{display:block;background-color:#ffffff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;}
|
||||
:-moz-placeholder{color:#999999;}
|
||||
::-webkit-input-placeholder{color:#999999;}
|
||||
.help-block,.help-inline{color:#555555;}
|
||||
.help-block{display:block;margin-bottom:9px;}
|
||||
.help-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding-left:5px;}
|
||||
.input-prepend,.input-append{margin-bottom:5px;}.input-prepend input,.input-append input,.input-prepend select,.input-append select,.input-prepend .uneditable-input,.input-append .uneditable-input{*margin-left:0;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}.input-prepend input:focus,.input-append input:focus,.input-prepend select:focus,.input-append select:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{position:relative;z-index:2;}
|
||||
.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc;}
|
||||
.input-prepend .add-on,.input-append .add-on{display:inline-block;width:auto;min-width:16px;height:18px;padding:4px 5px;font-weight:normal;line-height:18px;text-align:center;text-shadow:0 1px 0 #ffffff;vertical-align:middle;background-color:#eeeeee;border:1px solid #ccc;}
|
||||
.input-prepend .add-on,.input-append .add-on,.input-prepend .btn,.input-append .btn{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
|
||||
.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546;}
|
||||
.input-prepend .add-on,.input-prepend .btn{margin-right:-1px;}
|
||||
.input-append input,.input-append select .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
|
||||
.input-append .uneditable-input{border-left-color:#eee;border-right-color:#ccc;}
|
||||
.input-append .add-on,.input-append .btn{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
|
||||
.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
|
||||
.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
|
||||
.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
|
||||
.search-query{padding-left:14px;padding-right:14px;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;}
|
||||
.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;margin-bottom:0;}
|
||||
.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;}
|
||||
.form-search label,.form-inline label{display:inline-block;}
|
||||
.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0;}
|
||||
.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle;}
|
||||
.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-left:0;margin-right:3px;}
|
||||
.control-group{margin-bottom:9px;}
|
||||
legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate;}
|
||||
.form-horizontal .control-group{margin-bottom:18px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";}
|
||||
.form-horizontal .control-group:after{clear:both;}
|
||||
.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right;}
|
||||
.form-horizontal .controls{margin-left:160px;*display:inline-block;*margin-left:0;*padding-left:20px;}
|
||||
.form-horizontal .help-block{margin-top:9px;margin-bottom:0;}
|
||||
.form-horizontal .form-actions{padding-left:160px;}
|
||||
table{max-width:100%;border-collapse:collapse;border-spacing:0;background-color:transparent;}
|
||||
.table{width:100%;margin-bottom:18px;}.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #dddddd;}
|
||||
.table th{font-weight:bold;}
|
||||
.table thead th{vertical-align:bottom;}
|
||||
.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0;}
|
||||
.table tbody+tbody{border-top:2px solid #dddddd;}
|
||||
.table-condensed th,.table-condensed td{padding:4px 5px;}
|
||||
.table-bordered{border:1px solid #dddddd;border-left:0;border-collapse:separate;*border-collapse:collapsed;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th,.table-bordered td{border-left:1px solid #dddddd;}
|
||||
.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;}
|
||||
.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;}
|
||||
.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;}
|
||||
.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;}
|
||||
.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;}
|
||||
.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;}
|
||||
.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5;}
|
||||
table .span1{float:none;width:44px;margin-left:0;}
|
||||
table .span2{float:none;width:124px;margin-left:0;}
|
||||
table .span3{float:none;width:204px;margin-left:0;}
|
||||
table .span4{float:none;width:284px;margin-left:0;}
|
||||
table .span5{float:none;width:364px;margin-left:0;}
|
||||
table .span6{float:none;width:444px;margin-left:0;}
|
||||
table .span7{float:none;width:524px;margin-left:0;}
|
||||
table .span8{float:none;width:604px;margin-left:0;}
|
||||
table .span9{float:none;width:684px;margin-left:0;}
|
||||
table .span10{float:none;width:764px;margin-left:0;}
|
||||
table .span11{float:none;width:844px;margin-left:0;}
|
||||
table .span12{float:none;width:924px;margin-left:0;}
|
||||
table .span13{float:none;width:1004px;margin-left:0;}
|
||||
table .span14{float:none;width:1084px;margin-left:0;}
|
||||
table .span15{float:none;width:1164px;margin-left:0;}
|
||||
table .span16{float:none;width:1244px;margin-left:0;}
|
||||
table .span17{float:none;width:1324px;margin-left:0;}
|
||||
table .span18{float:none;width:1404px;margin-left:0;}
|
||||
table .span19{float:none;width:1484px;margin-left:0;}
|
||||
table .span20{float:none;width:1564px;margin-left:0;}
|
||||
table .span21{float:none;width:1644px;margin-left:0;}
|
||||
table .span22{float:none;width:1724px;margin-left:0;}
|
||||
table .span23{float:none;width:1804px;margin-left:0;}
|
||||
table .span24{float:none;width:1884px;margin-left:0;}
|
||||
[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;*margin-right:.3em;}[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0;}
|
||||
.icon-white{background-image:url("../img/glyphicons-halflings-white.png");}
|
||||
.icon-glass{background-position:0 0;}
|
||||
.icon-music{background-position:-24px 0;}
|
||||
.icon-search{background-position:-48px 0;}
|
||||
.icon-envelope{background-position:-72px 0;}
|
||||
.icon-heart{background-position:-96px 0;}
|
||||
.icon-star{background-position:-120px 0;}
|
||||
.icon-star-empty{background-position:-144px 0;}
|
||||
.icon-user{background-position:-168px 0;}
|
||||
.icon-film{background-position:-192px 0;}
|
||||
.icon-th-large{background-position:-216px 0;}
|
||||
.icon-th{background-position:-240px 0;}
|
||||
.icon-th-list{background-position:-264px 0;}
|
||||
.icon-ok{background-position:-288px 0;}
|
||||
.icon-remove{background-position:-312px 0;}
|
||||
.icon-zoom-in{background-position:-336px 0;}
|
||||
.icon-zoom-out{background-position:-360px 0;}
|
||||
.icon-off{background-position:-384px 0;}
|
||||
.icon-signal{background-position:-408px 0;}
|
||||
.icon-cog{background-position:-432px 0;}
|
||||
.icon-trash{background-position:-456px 0;}
|
||||
.icon-home{background-position:0 -24px;}
|
||||
.icon-file{background-position:-24px -24px;}
|
||||
.icon-time{background-position:-48px -24px;}
|
||||
.icon-road{background-position:-72px -24px;}
|
||||
.icon-download-alt{background-position:-96px -24px;}
|
||||
.icon-download{background-position:-120px -24px;}
|
||||
.icon-upload{background-position:-144px -24px;}
|
||||
.icon-inbox{background-position:-168px -24px;}
|
||||
.icon-play-circle{background-position:-192px -24px;}
|
||||
.icon-repeat{background-position:-216px -24px;}
|
||||
.icon-refresh{background-position:-240px -24px;}
|
||||
.icon-list-alt{background-position:-264px -24px;}
|
||||
.icon-lock{background-position:-287px -24px;}
|
||||
.icon-flag{background-position:-312px -24px;}
|
||||
.icon-headphones{background-position:-336px -24px;}
|
||||
.icon-volume-off{background-position:-360px -24px;}
|
||||
.icon-volume-down{background-position:-384px -24px;}
|
||||
.icon-volume-up{background-position:-408px -24px;}
|
||||
.icon-qrcode{background-position:-432px -24px;}
|
||||
.icon-barcode{background-position:-456px -24px;}
|
||||
.icon-tag{background-position:0 -48px;}
|
||||
.icon-tags{background-position:-25px -48px;}
|
||||
.icon-book{background-position:-48px -48px;}
|
||||
.icon-bookmark{background-position:-72px -48px;}
|
||||
.icon-print{background-position:-96px -48px;}
|
||||
.icon-camera{background-position:-120px -48px;}
|
||||
.icon-font{background-position:-144px -48px;}
|
||||
.icon-bold{background-position:-167px -48px;}
|
||||
.icon-italic{background-position:-192px -48px;}
|
||||
.icon-text-height{background-position:-216px -48px;}
|
||||
.icon-text-width{background-position:-240px -48px;}
|
||||
.icon-align-left{background-position:-264px -48px;}
|
||||
.icon-align-center{background-position:-288px -48px;}
|
||||
.icon-align-right{background-position:-312px -48px;}
|
||||
.icon-align-justify{background-position:-336px -48px;}
|
||||
.icon-list{background-position:-360px -48px;}
|
||||
.icon-indent-left{background-position:-384px -48px;}
|
||||
.icon-indent-right{background-position:-408px -48px;}
|
||||
.icon-facetime-video{background-position:-432px -48px;}
|
||||
.icon-picture{background-position:-456px -48px;}
|
||||
.icon-pencil{background-position:0 -72px;}
|
||||
.icon-map-marker{background-position:-24px -72px;}
|
||||
.icon-adjust{background-position:-48px -72px;}
|
||||
.icon-tint{background-position:-72px -72px;}
|
||||
.icon-edit{background-position:-96px -72px;}
|
||||
.icon-share{background-position:-120px -72px;}
|
||||
.icon-check{background-position:-144px -72px;}
|
||||
.icon-move{background-position:-168px -72px;}
|
||||
.icon-step-backward{background-position:-192px -72px;}
|
||||
.icon-fast-backward{background-position:-216px -72px;}
|
||||
.icon-backward{background-position:-240px -72px;}
|
||||
.icon-play{background-position:-264px -72px;}
|
||||
.icon-pause{background-position:-288px -72px;}
|
||||
.icon-stop{background-position:-312px -72px;}
|
||||
.icon-forward{background-position:-336px -72px;}
|
||||
.icon-fast-forward{background-position:-360px -72px;}
|
||||
.icon-step-forward{background-position:-384px -72px;}
|
||||
.icon-eject{background-position:-408px -72px;}
|
||||
.icon-chevron-left{background-position:-432px -72px;}
|
||||
.icon-chevron-right{background-position:-456px -72px;}
|
||||
.icon-plus-sign{background-position:0 -96px;}
|
||||
.icon-minus-sign{background-position:-24px -96px;}
|
||||
.icon-remove-sign{background-position:-48px -96px;}
|
||||
.icon-ok-sign{background-position:-72px -96px;}
|
||||
.icon-question-sign{background-position:-96px -96px;}
|
||||
.icon-info-sign{background-position:-120px -96px;}
|
||||
.icon-screenshot{background-position:-144px -96px;}
|
||||
.icon-remove-circle{background-position:-168px -96px;}
|
||||
.icon-ok-circle{background-position:-192px -96px;}
|
||||
.icon-ban-circle{background-position:-216px -96px;}
|
||||
.icon-arrow-left{background-position:-240px -96px;}
|
||||
.icon-arrow-right{background-position:-264px -96px;}
|
||||
.icon-arrow-up{background-position:-289px -96px;}
|
||||
.icon-arrow-down{background-position:-312px -96px;}
|
||||
.icon-share-alt{background-position:-336px -96px;}
|
||||
.icon-resize-full{background-position:-360px -96px;}
|
||||
.icon-resize-small{background-position:-384px -96px;}
|
||||
.icon-plus{background-position:-408px -96px;}
|
||||
.icon-minus{background-position:-433px -96px;}
|
||||
.icon-asterisk{background-position:-456px -96px;}
|
||||
.icon-exclamation-sign{background-position:0 -120px;}
|
||||
.icon-gift{background-position:-24px -120px;}
|
||||
.icon-leaf{background-position:-48px -120px;}
|
||||
.icon-fire{background-position:-72px -120px;}
|
||||
.icon-eye-open{background-position:-96px -120px;}
|
||||
.icon-eye-close{background-position:-120px -120px;}
|
||||
.icon-warning-sign{background-position:-144px -120px;}
|
||||
.icon-plane{background-position:-168px -120px;}
|
||||
.icon-calendar{background-position:-192px -120px;}
|
||||
.icon-random{background-position:-216px -120px;}
|
||||
.icon-comment{background-position:-240px -120px;}
|
||||
.icon-magnet{background-position:-264px -120px;}
|
||||
.icon-chevron-up{background-position:-288px -120px;}
|
||||
.icon-chevron-down{background-position:-313px -119px;}
|
||||
.icon-retweet{background-position:-336px -120px;}
|
||||
.icon-shopping-cart{background-position:-360px -120px;}
|
||||
.icon-folder-close{background-position:-384px -120px;}
|
||||
.icon-folder-open{background-position:-408px -120px;}
|
||||
.icon-resize-vertical{background-position:-432px -119px;}
|
||||
.icon-resize-horizontal{background-position:-456px -118px;}
|
||||
.dropdown{position:relative;}
|
||||
.dropdown-toggle{*margin-bottom:-3px;}
|
||||
.dropdown-toggle:active,.open .dropdown-toggle{outline:0;}
|
||||
.caret{display:inline-block;width:0;height:0;vertical-align:top;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000000;opacity:0.3;filter:alpha(opacity=30);content:"";}
|
||||
.dropdown .caret{margin-top:8px;margin-left:2px;}
|
||||
.dropdown:hover .caret,.open.dropdown .caret{opacity:1;filter:alpha(opacity=100);}
|
||||
.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;float:left;display:none;min-width:160px;padding:4px 0;margin:0;list-style:none;background-color:#ffffff;border-color:#ccc;border-color:rgba(0, 0, 0, 0.2);border-style:solid;border-width:1px;-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;*border-right-width:2px;*border-bottom-width:2px;}.dropdown-menu.pull-right{right:0;left:auto;}
|
||||
.dropdown-menu .divider{height:1px;margin:8px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;}
|
||||
.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#333333;white-space:nowrap;}
|
||||
.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;background-color:#0088cc;}
|
||||
.dropdown.open{*z-index:1000;}.dropdown.open .dropdown-toggle{color:#ffffff;background:#ccc;background:rgba(0, 0, 0, 0.3);}
|
||||
.dropdown.open .dropdown-menu{display:block;}
|
||||
.pull-right .dropdown-menu{left:auto;right:0;}
|
||||
.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:"\2191";}
|
||||
.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px;}
|
||||
.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||
.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
|
||||
.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
|
||||
.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
|
||||
.fade{-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;}
|
||||
.collapse{-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-ms-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;position:relative;overflow:hidden;height:0;}.collapse.in{height:auto;}
|
||||
.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;opacity:0.4;filter:alpha(opacity=40);cursor:pointer;}
|
||||
.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 10px 4px;margin-bottom:0;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);vertical-align:middle;background-color:#f5f5f5;background-image:-moz-linear-gradient(top, #ffffff, #e6e6e6);background-image:-ms-linear-gradient(top, #ffffff, #e6e6e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(top, #ffffff, #e6e6e6);background-image:-o-linear-gradient(top, #ffffff, #e6e6e6);background-image:linear-gradient(top, #ffffff, #e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);border:1px solid #cccccc;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;*margin-left:.3em;}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;}
|
||||
.btn:active,.btn.active{background-color:#cccccc \9;}
|
||||
.btn:first-child{*margin-left:0;}
|
||||
.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;}
|
||||
.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
|
||||
.btn.active,.btn:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;outline:0;}
|
||||
.btn.disabled,.btn[disabled]{cursor:default;background-image:none;background-color:#e6e6e6;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
|
||||
.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
|
||||
.btn-large [class^="icon-"]{margin-top:1px;}
|
||||
.btn-small{padding:5px 9px;font-size:11px;line-height:16px;}
|
||||
.btn-small [class^="icon-"]{margin-top:-1px;}
|
||||
.btn-mini{padding:2px 6px;font-size:11px;line-height:14px;}
|
||||
.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;}
|
||||
.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255, 255, 255, 0.75);}
|
||||
.btn-primary{background-color:#0074cc;background-image:-moz-linear-gradient(top, #0088cc, #0055cc);background-image:-ms-linear-gradient(top, #0088cc, #0055cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0055cc));background-image:-webkit-linear-gradient(top, #0088cc, #0055cc);background-image:-o-linear-gradient(top, #0088cc, #0055cc);background-image:linear-gradient(top, #0088cc, #0055cc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0);border-color:#0055cc #0055cc #003580;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#0055cc;}
|
||||
.btn-primary:active,.btn-primary.active{background-color:#004099 \9;}
|
||||
.btn-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;}
|
||||
.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;}
|
||||
.btn-danger{background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;}
|
||||
.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;}
|
||||
.btn-success{background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;}
|
||||
.btn-success:active,.btn-success.active{background-color:#408140 \9;}
|
||||
.btn-info{background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;}
|
||||
.btn-info:active,.btn-info.active{background-color:#24748c \9;}
|
||||
.btn-inverse{background-color:#414141;background-image:-moz-linear-gradient(top, #555555, #222222);background-image:-ms-linear-gradient(top, #555555, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#222222));background-image:-webkit-linear-gradient(top, #555555, #222222);background-image:-o-linear-gradient(top, #555555, #222222);background-image:linear-gradient(top, #555555, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#222222;}
|
||||
.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9;}
|
||||
button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;}
|
||||
button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px;}
|
||||
button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px;}
|
||||
button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px;}
|
||||
.btn-group{position:relative;*zoom:1;*margin-left:.3em;}.btn-group:before,.btn-group:after{display:table;content:"";}
|
||||
.btn-group:after{clear:both;}
|
||||
.btn-group:first-child{*margin-left:0;}
|
||||
.btn-group+.btn-group{margin-left:5px;}
|
||||
.btn-toolbar{margin-top:9px;margin-bottom:9px;}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;}
|
||||
.btn-group .btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
|
||||
.btn-group .btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
|
||||
.btn-group .btn:last-child,.btn-group .dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;}
|
||||
.btn-group .btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;}
|
||||
.btn-group .btn.large:last-child,.btn-group .large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;}
|
||||
.btn-group .btn:hover,.btn-group .btn:focus,.btn-group .btn:active,.btn-group .btn.active{z-index:2;}
|
||||
.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;}
|
||||
.btn-group .dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);*padding-top:3px;*padding-bottom:3px;}
|
||||
.btn-group .btn-mini.dropdown-toggle{padding-left:5px;padding-right:5px;*padding-top:1px;*padding-bottom:1px;}
|
||||
.btn-group .btn-small.dropdown-toggle{*padding-top:4px;*padding-bottom:4px;}
|
||||
.btn-group .btn-large.dropdown-toggle{padding-left:12px;padding-right:12px;}
|
||||
.btn-group.open{*z-index:1000;}.btn-group.open .dropdown-menu{display:block;margin-top:1px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
|
||||
.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);}
|
||||
.btn .caret{margin-top:7px;margin-left:0;}
|
||||
.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100);}
|
||||
.btn-mini .caret{margin-top:5px;}
|
||||
.btn-small .caret{margin-top:6px;}
|
||||
.btn-large .caret{margin-top:6px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
|
||||
.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:0.75;filter:alpha(opacity=75);}
|
||||
.alert{padding:8px 35px 8px 14px;margin-bottom:18px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;color:#c09853;}
|
||||
.alert-heading{color:inherit;}
|
||||
.alert .close{position:relative;top:-2px;right:-21px;line-height:18px;}
|
||||
.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847;}
|
||||
.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48;}
|
||||
.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad;}
|
||||
.alert-block{padding-top:14px;padding-bottom:14px;}
|
||||
.alert-block>p,.alert-block>ul{margin-bottom:0;}
|
||||
.alert-block p+p{margin-top:5px;}
|
||||
.nav{margin-left:0;margin-bottom:18px;list-style:none;}
|
||||
.nav>li>a{display:block;}
|
||||
.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;}
|
||||
.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;}
|
||||
.nav li+.nav-header{margin-top:9px;}
|
||||
.nav-list{padding-left:15px;padding-right:15px;margin-bottom:0;}
|
||||
.nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
|
||||
.nav-list>li>a{padding:3px 15px;}
|
||||
.nav-list>.active>a,.nav-list>.active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;}
|
||||
.nav-list [class^="icon-"]{margin-right:2px;}
|
||||
.nav-list .divider{height:1px;margin:8px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;}
|
||||
.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";}
|
||||
.nav-tabs:after,.nav-pills:after{clear:both;}
|
||||
.nav-tabs>li,.nav-pills>li{float:left;}
|
||||
.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;}
|
||||
.nav-tabs{border-bottom:1px solid #ddd;}
|
||||
.nav-tabs>li{margin-bottom:-1px;}
|
||||
.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:18px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;}
|
||||
.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;}
|
||||
.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
|
||||
.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#ffffff;background-color:#0088cc;}
|
||||
.nav-stacked>li{float:none;}
|
||||
.nav-stacked>li>a{margin-right:0;}
|
||||
.nav-tabs.nav-stacked{border-bottom:0;}
|
||||
.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
|
||||
.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
|
||||
.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
|
||||
.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;}
|
||||
.nav-pills.nav-stacked>li>a{margin-bottom:3px;}
|
||||
.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;}
|
||||
.nav-tabs .dropdown-menu,.nav-pills .dropdown-menu{margin-top:1px;border-width:1px;}
|
||||
.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||
.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{border-top-color:#0088cc;border-bottom-color:#0088cc;margin-top:6px;}
|
||||
.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580;}
|
||||
.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;border-bottom-color:#333333;}
|
||||
.nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;}
|
||||
.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;}
|
||||
.nav .open .caret,.nav .open.active .caret,.nav .open a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:1;filter:alpha(opacity=100);}
|
||||
.tabs-stacked .open>a:hover{border-color:#999999;}
|
||||
.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";}
|
||||
.tabbable:after{clear:both;}
|
||||
.tab-content{display:table;width:100%;}
|
||||
.tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs{border-bottom:0;}
|
||||
.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;}
|
||||
.tab-content>.active,.pill-content>.active{display:block;}
|
||||
.tabs-below .nav-tabs{border-top:1px solid #ddd;}
|
||||
.tabs-below .nav-tabs>li{margin-top:-1px;margin-bottom:0;}
|
||||
.tabs-below .nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below .nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;}
|
||||
.tabs-below .nav-tabs .active>a,.tabs-below .nav-tabs .active>a:hover{border-color:transparent #ddd #ddd #ddd;}
|
||||
.tabs-left .nav-tabs>li,.tabs-right .nav-tabs>li{float:none;}
|
||||
.tabs-left .nav-tabs>li>a,.tabs-right .nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;}
|
||||
.tabs-left .nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;}
|
||||
.tabs-left .nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
|
||||
.tabs-left .nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;}
|
||||
.tabs-left .nav-tabs .active>a,.tabs-left .nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;}
|
||||
.tabs-right .nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;}
|
||||
.tabs-right .nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
|
||||
.tabs-right .nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;}
|
||||
.tabs-right .nav-tabs .active>a,.tabs-right .nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;}
|
||||
.navbar{*position:relative;*z-index:2;overflow:visible;margin-bottom:18px;}
|
||||
.navbar-inner{padding-left:20px;padding-right:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);}
|
||||
.navbar .container{width:auto;}
|
||||
.btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}.btn-navbar:hover,.btn-navbar:active,.btn-navbar.active,.btn-navbar.disabled,.btn-navbar[disabled]{background-color:#222222;}
|
||||
.btn-navbar:active,.btn-navbar.active{background-color:#080808 \9;}
|
||||
.btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);}
|
||||
.btn-navbar .icon-bar+.icon-bar{margin-top:3px;}
|
||||
.nav-collapse.collapse{height:auto;}
|
||||
.navbar{color:#999999;}.navbar .brand:hover{text-decoration:none;}
|
||||
.navbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#ffffff;}
|
||||
.navbar .navbar-text{margin-bottom:0;line-height:40px;}
|
||||
.navbar .btn,.navbar .btn-group{margin-top:5px;}
|
||||
.navbar .btn-group .btn{margin-top:0;}
|
||||
.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";}
|
||||
.navbar-form:after{clear:both;}
|
||||
.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;}
|
||||
.navbar-form input,.navbar-form select{display:inline-block;margin-bottom:0;}
|
||||
.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;}
|
||||
.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap;}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;}
|
||||
.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;background-color:#626262;border:1px solid #151515;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.navbar-search .search-query:-moz-placeholder{color:#cccccc;}
|
||||
.navbar-search .search-query::-webkit-input-placeholder{color:#cccccc;}
|
||||
.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;}
|
||||
.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0;}
|
||||
.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
|
||||
.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
|
||||
.navbar-fixed-top{top:0;}
|
||||
.navbar-fixed-bottom{bottom:0;}
|
||||
.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;}
|
||||
.navbar .nav.pull-right{float:right;}
|
||||
.navbar .nav>li{display:block;float:left;}
|
||||
.navbar .nav>li>a{float:none;padding:10px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}
|
||||
.navbar .nav>li>a:hover{background-color:transparent;color:#ffffff;text-decoration:none;}
|
||||
.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;}
|
||||
.navbar .divider-vertical{height:40px;width:1px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;}
|
||||
.navbar .nav.pull-right{margin-left:10px;margin-right:0;}
|
||||
.navbar .dropdown-menu{margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.navbar .dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;}
|
||||
.navbar .dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;}
|
||||
.navbar-fixed-bottom .dropdown-menu:before{border-top:7px solid #ccc;border-top-color:rgba(0, 0, 0, 0.2);border-bottom:0;bottom:-7px;top:auto;}
|
||||
.navbar-fixed-bottom .dropdown-menu:after{border-top:6px solid #ffffff;border-bottom:0;bottom:-6px;top:auto;}
|
||||
.navbar .nav .dropdown-toggle .caret,.navbar .nav .open.dropdown .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
|
||||
.navbar .nav .active .caret{opacity:1;filter:alpha(opacity=100);}
|
||||
.navbar .nav .open>.dropdown-toggle,.navbar .nav .active>.dropdown-toggle,.navbar .nav .open.active>.dropdown-toggle{background-color:transparent;}
|
||||
.navbar .nav .active>.dropdown-toggle:hover{color:#ffffff;}
|
||||
.navbar .nav.pull-right .dropdown-menu,.navbar .nav .dropdown-menu.pull-right{left:auto;right:0;}.navbar .nav.pull-right .dropdown-menu:before,.navbar .nav .dropdown-menu.pull-right:before{left:auto;right:12px;}
|
||||
.navbar .nav.pull-right .dropdown-menu:after,.navbar .nav .dropdown-menu.pull-right:after{left:auto;right:13px;}
|
||||
.breadcrumb{padding:7px 14px;margin:0 0 18px;list-style:none;background-color:#fbfbfb;background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 0 #ffffff;}
|
||||
.breadcrumb .divider{padding:0 5px;color:#999999;}
|
||||
.breadcrumb .active a{color:#333333;}
|
||||
.pagination{height:36px;margin:18px 0;}
|
||||
.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}
|
||||
.pagination li{display:inline;}
|
||||
.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0;}
|
||||
.pagination a:hover,.pagination .active a{background-color:#f5f5f5;}
|
||||
.pagination .active a{color:#999999;cursor:default;}
|
||||
.pagination .disabled span,.pagination .disabled a,.pagination .disabled a:hover{color:#999999;background-color:transparent;cursor:default;}
|
||||
.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
|
||||
.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
|
||||
.pagination-centered{text-align:center;}
|
||||
.pagination-right{text-align:right;}
|
||||
.pager{margin-left:0;margin-bottom:18px;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";}
|
||||
.pager:after{clear:both;}
|
||||
.pager li{display:inline;}
|
||||
.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
|
||||
.pager a:hover{text-decoration:none;background-color:#f5f5f5;}
|
||||
.pager .next a{float:right;}
|
||||
.pager .previous a{float:left;}
|
||||
.pager .disabled a,.pager .disabled a:hover{color:#999999;background-color:#fff;cursor:default;}
|
||||
.modal-open .dropdown-menu{z-index:2050;}
|
||||
.modal-open .dropdown.open{*z-index:2050;}
|
||||
.modal-open .popover{z-index:2060;}
|
||||
.modal-open .tooltip{z-index:2070;}
|
||||
.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;}
|
||||
.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);}
|
||||
.modal{position:fixed;top:50%;left:50%;z-index:1050;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;}
|
||||
.modal.fade.in{top:50%;}
|
||||
.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;}
|
||||
.modal-body{overflow-y:auto;max-height:400px;padding:15px;}
|
||||
.modal-form{margin-bottom:0;}
|
||||
.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";}
|
||||
.modal-footer:after{clear:both;}
|
||||
.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0;}
|
||||
.modal-footer .btn-group .btn+.btn{margin-left:-1px;}
|
||||
.tooltip{position:absolute;z-index:1020;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);}
|
||||
.tooltip.top{margin-top:-2px;}
|
||||
.tooltip.right{margin-left:2px;}
|
||||
.tooltip.bottom{margin-top:2px;}
|
||||
.tooltip.left{margin-left:-2px;}
|
||||
.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
|
||||
.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
|
||||
.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
|
||||
.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
|
||||
.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||
.tooltip-arrow{position:absolute;width:0;height:0;}
|
||||
.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px;}.popover.top{margin-top:-5px;}
|
||||
.popover.right{margin-left:5px;}
|
||||
.popover.bottom{margin-top:5px;}
|
||||
.popover.left{margin-left:-5px;}
|
||||
.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
|
||||
.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
|
||||
.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
|
||||
.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
|
||||
.popover .arrow{position:absolute;width:0;height:0;}
|
||||
.popover-inner{padding:3px;width:280px;overflow:hidden;background:#000000;background:rgba(0, 0, 0, 0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);}
|
||||
.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;}
|
||||
.popover-content{padding:14px;background-color:#ffffff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;}
|
||||
.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";}
|
||||
.thumbnails:after{clear:both;}
|
||||
.thumbnails>li{float:left;margin:0 0 18px 20px;}
|
||||
.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);}
|
||||
a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
|
||||
.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;}
|
||||
.thumbnail .caption{padding:9px;}
|
||||
.label{padding:1px 4px 2px;font-size:10.998px;font-weight:bold;line-height:13px;color:#ffffff;vertical-align:middle;white-space:nowrap;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
|
||||
.label:hover{color:#ffffff;text-decoration:none;}
|
||||
.label-important{background-color:#b94a48;}
|
||||
.label-important:hover{background-color:#953b39;}
|
||||
.label-warning{background-color:#f89406;}
|
||||
.label-warning:hover{background-color:#c67605;}
|
||||
.label-success{background-color:#468847;}
|
||||
.label-success:hover{background-color:#356635;}
|
||||
.label-info{background-color:#3a87ad;}
|
||||
.label-info:hover{background-color:#2d6987;}
|
||||
.label-inverse{background-color:#333333;}
|
||||
.label-inverse:hover{background-color:#1a1a1a;}
|
||||
.badge{padding:1px 9px 2px;font-size:12.025px;font-weight:bold;white-space:nowrap;color:#ffffff;background-color:#999999;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;}
|
||||
.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;}
|
||||
.badge-error{background-color:#b94a48;}
|
||||
.badge-error:hover{background-color:#953b39;}
|
||||
.badge-warning{background-color:#f89406;}
|
||||
.badge-warning:hover{background-color:#c67605;}
|
||||
.badge-success{background-color:#468847;}
|
||||
.badge-success:hover{background-color:#356635;}
|
||||
.badge-info{background-color:#3a87ad;}
|
||||
.badge-info:hover{background-color:#2d6987;}
|
||||
.badge-inverse{background-color:#333333;}
|
||||
.badge-inverse:hover{background-color:#1a1a1a;}
|
||||
@-webkit-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@-ms-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}.progress{overflow:hidden;height:18px;margin-bottom:18px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-ms-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(top, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||
.progress .bar{width:0%;height:18px;color:#ffffff;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-ms-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(top, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-ms-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;}
|
||||
.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;}
|
||||
.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;}
|
||||
.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);}
|
||||
.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
|
||||
.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);}
|
||||
.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
|
||||
.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);}
|
||||
.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
|
||||
.progress-warning .bar{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);}
|
||||
.progress-warning.progress-striped .bar{background-color:#fbb450;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
|
||||
.accordion{margin-bottom:18px;}
|
||||
.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
|
||||
.accordion-heading{border-bottom:0;}
|
||||
.accordion-heading .accordion-toggle{display:block;padding:8px 15px;}
|
||||
.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;}
|
||||
.carousel{position:relative;margin-bottom:18px;line-height:1;}
|
||||
.carousel-inner{overflow:hidden;width:100%;position:relative;}
|
||||
.carousel .item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-ms-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;}
|
||||
.carousel .item>img{display:block;line-height:1;}
|
||||
.carousel .active,.carousel .next,.carousel .prev{display:block;}
|
||||
.carousel .active{left:0;}
|
||||
.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;}
|
||||
.carousel .next{left:100%;}
|
||||
.carousel .prev{left:-100%;}
|
||||
.carousel .next.left,.carousel .prev.right{left:0;}
|
||||
.carousel .active.left{left:-100%;}
|
||||
.carousel .active.right{left:100%;}
|
||||
.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;}
|
||||
.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);}
|
||||
.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:10px 15px 5px;background:#333333;background:rgba(0, 0, 0, 0.75);}
|
||||
.carousel-caption h4,.carousel-caption p{color:#ffffff;}
|
||||
.hero-unit{padding:60px;margin-bottom:30px;background-color:#eeeeee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;color:inherit;letter-spacing:-1px;}
|
||||
.hero-unit p{font-size:18px;font-weight:200;line-height:27px;color:inherit;}
|
||||
.pull-right{float:right;}
|
||||
.pull-left{float:left;}
|
||||
.hide{display:none;}
|
||||
.show{display:block;}
|
||||
.invisible{visibility:hidden;}
|
12
zerobin/static/css/bootswatch.4.5.css
Normal file
34
zerobin/static/css/desert.css
Normal file
@ -0,0 +1,34 @@
|
||||
/* desert scheme ported from vim to google prettify */
|
||||
pre.prettyprint { display: block; background-color: #333 }
|
||||
pre .nocode { background-color: none; color: #000 }
|
||||
pre .str { color: #ffa0a0 } /* string - pink */
|
||||
pre .kwd { color: #f0e68c; font-weight: bold }
|
||||
pre .com { color: #87ceeb } /* comment - skyblue */
|
||||
pre .typ { color: #98fb98 } /* type - lightgreen */
|
||||
pre .lit { color: #cd5c5c } /* literal - darkred */
|
||||
pre .pun { color: #fff } /* punctuation */
|
||||
pre .pln { color: #fff } /* plaintext */
|
||||
pre .tag { color: #f0e68c; font-weight: bold } /* html/xml tag - lightyellow */
|
||||
pre .atn { color: #bdb76b; font-weight: bold } /* attribute name - khaki */
|
||||
pre .atv { color: #ffa0a0 } /* attribute value - pink */
|
||||
pre .dec { color: #98fb98 } /* decimal - lightgreen */
|
||||
|
||||
/* Specify class=linenums on a pre to get line numbering */
|
||||
ol.linenums { margin-top: 0; margin-bottom: 0; color: #AEAEAE } /* IE indents via margin-left */
|
||||
li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8 { list-style-type: none }
|
||||
/* Alternate shading for lines */
|
||||
li.L1,li.L3,li.L5,li.L7,li.L9 { }
|
||||
|
||||
@media print {
|
||||
pre.prettyprint { background-color: none }
|
||||
pre .str, code .str { color: #060 }
|
||||
pre .kwd, code .kwd { color: #006; font-weight: bold }
|
||||
pre .com, code .com { color: #600; font-style: italic }
|
||||
pre .typ, code .typ { color: #404; font-weight: bold }
|
||||
pre .lit, code .lit { color: #044 }
|
||||
pre .pun, code .pun { color: #440 }
|
||||
pre .pln, code .pln { color: #000 }
|
||||
pre .tag, code .tag { color: #006; font-weight: bold }
|
||||
pre .atn, code .atn { color: #404 }
|
||||
pre .atv, code .atv { color: #060 }
|
||||
}
|
@ -8,18 +8,17 @@
|
||||
border: 25px solid red;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
/* logo */
|
||||
|
||||
.brand {
|
||||
font-size: 38px !important;
|
||||
padding: 0 55px 3px !important;
|
||||
padding: 0 !important;
|
||||
margin-left: 10%;
|
||||
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .1), 0 0 30px rgba(255, 255, 255, .125);
|
||||
-webkit-transition: all .2s linear;
|
||||
-moz-transition: all .2s linear;
|
||||
-o-transition: all .2s linear;
|
||||
-ms-transition: all .2s linear;
|
||||
transition: all .2s linear;
|
||||
}
|
||||
|
||||
.brand span {
|
||||
@ -51,9 +50,9 @@
|
||||
|
||||
/* body & other stuff */
|
||||
|
||||
body {
|
||||
padding-top: 60px;
|
||||
padding-bottom: 40px;
|
||||
.blk-space {
|
||||
height: 20px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sidebar-nav {
|
||||
@ -64,22 +63,18 @@ select {
|
||||
width: 135px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline;
|
||||
margin-left: 18px;
|
||||
font-style: italic;
|
||||
font-size: 11px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-left: -9px;
|
||||
a {
|
||||
color: #2ea1d7;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #1888bc;
|
||||
}
|
||||
|
||||
p {
|
||||
@ -100,48 +95,56 @@ blockquote {
|
||||
float: left;
|
||||
}
|
||||
|
||||
h4 p {
|
||||
float: left;
|
||||
font-size: 80px;
|
||||
text-shadow: 1px 3px 1px #DDD, 0 0 4px #333;
|
||||
-webkit-transition: all 0.2s linear;
|
||||
-o-transition: all .2s linear;
|
||||
-ms-transition: all .2s linear;
|
||||
-moz-transition: all .2s linear;
|
||||
transition: all .2s linear;
|
||||
margin-right: 7px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
h4#pixels-total {
|
||||
position: relative;
|
||||
width: 166px;
|
||||
float: right;
|
||||
margin: 8px 0 0 0;
|
||||
padding: 0 0 0 54px;
|
||||
font-size: 1.1em;
|
||||
line-height: 1.4;
|
||||
font-weight: normal;
|
||||
color: #777;
|
||||
-webkit-border-top-right-radius: 6px;
|
||||
-webkit-border-top-left-radius: 20px;
|
||||
-moz-border-top-right-radius: 6px;
|
||||
-moz-border-top-left-radius: 20px;
|
||||
border-top-right-radius: 6px;
|
||||
border-top-left-radius: 20px;
|
||||
}
|
||||
|
||||
.greetings {
|
||||
clear: both;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.alert .title {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
|
||||
/* See:
|
||||
https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/
|
||||
*/
|
||||
|
||||
#app {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#wrap-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
display: block;
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding: 8px 0 0 0;
|
||||
background-color: #424141;
|
||||
}
|
||||
|
||||
.footer li {
|
||||
list-style-type: none;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.footer li span {
|
||||
color: #2ea1d7;
|
||||
}
|
||||
|
||||
.footer li span:hover {
|
||||
color: #1888bc;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.footer li:not(:last-child):after {
|
||||
content: ' -';
|
||||
}
|
||||
|
||||
/* Home */
|
||||
|
||||
.btn-group {
|
||||
@ -172,23 +175,112 @@ input.btn-upload {
|
||||
|
||||
input.hide-upload {
|
||||
position: relative;
|
||||
left: -110px;
|
||||
left: -80px;
|
||||
-moz-opacity: 0;
|
||||
filter: alpha(opacity=0);
|
||||
opacity: 0;
|
||||
z-index: 2;
|
||||
width: 100px;
|
||||
width: 0;
|
||||
margin-top: -20px;
|
||||
cursor: pointer;
|
||||
cursor: hand;
|
||||
height: 49px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
/* Paste Page */
|
||||
|
||||
#paste-content {
|
||||
background-color: white;
|
||||
padding: 1em;
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.reader-mode-title {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.paste-options-res {
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
opacity: 0.9 !important;
|
||||
}
|
||||
|
||||
.reader-mode-tools {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.paste-options-res .btn-group {
|
||||
float: none;
|
||||
border: 0px;
|
||||
}
|
||||
.paste-options-res span {
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.paste-form {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#expiration-tag {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
margin: 1em;
|
||||
background: grey;
|
||||
color: white;
|
||||
font-size: 0.8em;
|
||||
padding: 0 1ex;
|
||||
opacity: 0.5;
|
||||
filter: alpha(opacity=50);
|
||||
font-weight: bold;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
-moz-background-clip: padding;
|
||||
-webkit-background-clip: padding-box;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.paste-options,
|
||||
.paste-options-res {
|
||||
background-color: #333333;
|
||||
padding: 10px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.paste-options-res svg {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.paste-options span {
|
||||
background-color: #3c4a59;
|
||||
color: #f9fafc;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.paste-options:hover{
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.paste-options input{
|
||||
background-color: #375A7F;
|
||||
color: #eee;
|
||||
}
|
||||
::placeholder {
|
||||
color: #f9fafc !important;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.paste-wrapper {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
.paste-wrapper img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.pre-wrapper {
|
||||
margin: 8px 0 8px 0;
|
||||
}
|
||||
|
||||
#paste-content.linenums {
|
||||
@ -199,8 +291,8 @@ input.hide-upload {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.paste-option {
|
||||
float: right;
|
||||
.tip svg{
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
a#clip-button.hover {
|
||||
@ -222,10 +314,15 @@ li.L9 {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.prettyprint.linenums {
|
||||
-webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
|
||||
-moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
|
||||
box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
|
||||
pre.prettyprint {
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
padding: 5px;
|
||||
word-break: break-word;
|
||||
margin-bottom: 0px;
|
||||
/* background-repeat: no-repeat;
|
||||
background-position: 0 calc(100% + 6px);
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 330'%3E%3Cpath fill='%23273036' fill-opacity='0.1' d='M0,128L40,154.7C80,181,160,235,240,266.7C320,299,400,309,480,304C560,299,640,277,720,240C800,203,880,149,960,160C1040,171,1120,245,1200,261.3C1280,277,1360,235,1400,213.3L1440,192L1440,320L1400,320C1360,320,1280,320,1200,320C1120,320,1040,320,960,320C880,320,800,320,720,320C640,320,560,320,480,320C400,320,320,320,240,320C160,320,80,320,40,320L0,320Z'%3E%3C/path%3E%3C/svg%3E"); */
|
||||
}
|
||||
|
||||
/* Specify class=linenums on a pre to get line numbering */
|
||||
@ -234,48 +331,87 @@ ol.linenums {
|
||||
/* IE indents via margin-left */
|
||||
}
|
||||
|
||||
ol.linenums li {
|
||||
color: #bebec5;
|
||||
line-height: 18px;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
}
|
||||
|
||||
.prettyprint {
|
||||
padding: 8px;
|
||||
background-color: #f7f7f9;
|
||||
border: 1px solid #e1e1e8;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, monospace, serif;
|
||||
line-height: 21px;
|
||||
font-size: 12px;
|
||||
|
||||
white-space: pre-wrap;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.kwd {
|
||||
color: #66F;
|
||||
}
|
||||
|
||||
.pun,
|
||||
.opn,
|
||||
.clo {
|
||||
color: #0A0;
|
||||
}
|
||||
|
||||
.lit {
|
||||
color: #933;
|
||||
}
|
||||
|
||||
.com {
|
||||
color: #C0C;
|
||||
}
|
||||
|
||||
/* Common css */
|
||||
|
||||
form {
|
||||
padding-bottom: 3em !important;
|
||||
padding-right: 17px;
|
||||
|
||||
#faq {
|
||||
max-width: 80%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#buy_bitcoin {
|
||||
max-width: 80%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#buy_bitcoin img{
|
||||
width: 85%;
|
||||
display: block;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.caret {
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-top: 8px solid #f9fafc;
|
||||
width: 1px;
|
||||
margin: 8px 0px 0px 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.form-control,
|
||||
.form-control:focus,
|
||||
.form-control:disabled,
|
||||
.form-control[readonly],
|
||||
#paste-content {
|
||||
background-color: #375a7f;
|
||||
color: #f9fafc;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.upload-file {
|
||||
min-width: 100px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.download-link {
|
||||
border-top-left-radius: 3px !important;
|
||||
border-bottom-left-radius: 3px !important;
|
||||
}
|
||||
|
||||
.download-link a,
|
||||
.buy-btc a {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.download-link a:hover,
|
||||
.buy-btc a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.select-date {
|
||||
width: 320px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.select-date-clone {
|
||||
width: 60%;
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
.container-md {
|
||||
margin-top: 20px;
|
||||
padding-bottom: 130px;
|
||||
}
|
||||
|
||||
form textarea {
|
||||
@ -283,16 +419,6 @@ form textarea {
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
button.btn,
|
||||
input[type="submit"].btn {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.well {
|
||||
padding-bottom: 40px;
|
||||
padding-right: 17px;
|
||||
}
|
||||
|
||||
.legal {
|
||||
margin: 0 auto;
|
||||
width: 300px;
|
||||
@ -300,51 +426,28 @@ input[type="submit"].btn {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.btn-primary,
|
||||
.btn-danger {
|
||||
position: relative;
|
||||
top: -4px;
|
||||
#alert-template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/** Progress bar */
|
||||
|
||||
.progress {
|
||||
width: 100%;
|
||||
margin: 8px 0 8px 0;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.progress .bar {
|
||||
width: 25%;
|
||||
text-indent: 10px;
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.lnk-option canvas {
|
||||
vertical-align: middle;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* Previous paste list */
|
||||
|
||||
.previous-pastes .item {
|
||||
margin-top: 5px;
|
||||
vertical-align: middle;
|
||||
line-height: 24px;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
li.item {
|
||||
margin-left: -13px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
|
||||
.previous-pastes canvas {
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
.progress-container {
|
||||
margin: 8px 0 8px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
html.local-storage .no-local-storage {
|
||||
@ -377,24 +480,254 @@ canvas {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#expiration-tag {
|
||||
float: right;
|
||||
margin: 1em;
|
||||
background: grey;
|
||||
color: white;
|
||||
font-size: 0.8em;
|
||||
padding: 0 1ex;
|
||||
opacity: 0.5;
|
||||
filter: alpha(opacity=50);
|
||||
font-weight: bold;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
-moz-background-clip: padding;
|
||||
-webkit-background-clip: padding-box;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
#content.hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
/* top Menu */
|
||||
|
||||
.topnav {
|
||||
height: 60px;
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.topnav .brand {
|
||||
float: left;
|
||||
display: block;
|
||||
color: #f2f2f2;
|
||||
text-align: center;
|
||||
padding: 14px 16px;
|
||||
text-decoration: none;
|
||||
font-size: 17px;
|
||||
width: calc(22% - 30px);
|
||||
}
|
||||
|
||||
.topnav .tagline {
|
||||
font-size: 0.9em;
|
||||
padding: 8px;
|
||||
float: left;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.topnav .tagline span {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.topnav a.active {
|
||||
background-color: #4CAF50;
|
||||
color: #f9fafc;
|
||||
}
|
||||
|
||||
.topnav .icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Styling the sub menu */
|
||||
nav {
|
||||
float: right;
|
||||
margin: 18px;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
float: left;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
color: #f9fafc;
|
||||
background: #333333;
|
||||
padding: 2px 10px 0 10px;
|
||||
text-decoration: none;
|
||||
height: 30px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
nav ul li a:hover {
|
||||
background: #375A7F;
|
||||
border-radius: 3px;
|
||||
color: #FFF;
|
||||
text-decoration: none;
|
||||
|
||||
}
|
||||
|
||||
.submenu {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.submenu ul {
|
||||
position: absolute;
|
||||
margin: 6px 0 0px 0px;
|
||||
width: fill-available;
|
||||
z-index: 999;
|
||||
border: 1px solid #666;
|
||||
border-radius: 3px;
|
||||
padding: 0;
|
||||
width: 133px;
|
||||
border-top: 0px;
|
||||
}
|
||||
|
||||
.submenu li {
|
||||
display: block;
|
||||
background: #375A7F;
|
||||
color: white;
|
||||
float: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Styling drop down links */
|
||||
.submenu li a {
|
||||
background: transparent;
|
||||
padding: 10px;
|
||||
display: block;
|
||||
display: inline-table;
|
||||
width: -moz-available; /* WebKit-based browsers will ignore this. */
|
||||
width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
|
||||
width: fill-available;
|
||||
}
|
||||
|
||||
.submenu li a:hover,
|
||||
.submenu li.active {
|
||||
background: gray;
|
||||
}
|
||||
|
||||
/* Showing Submenu on hover*/
|
||||
.submenu:hover ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media screen and (max-width: 1060px) {
|
||||
.topnav a:not(:first-child) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.topnav .tagline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.brand {
|
||||
margin-left: 1%;
|
||||
}
|
||||
|
||||
/* Fonts */
|
||||
.btn,
|
||||
.btn-group span {
|
||||
font-size: 0.77em;
|
||||
}
|
||||
|
||||
|
||||
.reader-tools {
|
||||
bottom: 30px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
.responsive-icons svg{
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 660px) {
|
||||
.reader-mode .submenu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.reader-mode .reader-book {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.paste-wrapper img {
|
||||
width: 94%;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.paste-options-res a {
|
||||
max-width: 120px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media screen and (max-width: 460px) {
|
||||
|
||||
/* Responsive icons */
|
||||
.responsive-icons {
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.responsive-icons svg{
|
||||
display: block;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* reader mode */
|
||||
|
||||
#readable-paste-content {
|
||||
color: #f2f2f2;
|
||||
max-width: 800px;
|
||||
width: 90%;
|
||||
text-align: justify;
|
||||
margin: auto;
|
||||
margin-top: 20px;
|
||||
white-space: pre-wrap;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.reader-mode-bg {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.reader-book svg {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.reader-tools {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.reader-tools.min a{
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.reader-tools.max a{
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/* Admin */
|
||||
|
||||
.login-form {
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#password-field {
|
||||
width: calc(100% - 65px);
|
||||
}
|
||||
|
||||
.admin-header {
|
||||
text-align: center;
|
||||
font-size: 1.5em;
|
||||
color: #f2f2f2;
|
||||
margin: 20px 0px 20px 0px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.admin-header svg {
|
||||
font-size: 4em;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
form {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.logout svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 0px 10px 2px 0px;
|
||||
}
|
||||
|
13
zerobin/static/css/style.min.css
vendored
@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Derived from einaros's Sons of Obsidian theme at
|
||||
* http://studiostyl.es/schemes/son-of-obsidian by
|
||||
* Alex Ford of CodeTunnel:
|
||||
* http://CodeTunnel.com/blog/post/71/google-code-prettify-obsidian-theme
|
||||
*/
|
||||
|
||||
.str
|
||||
{
|
||||
color: #EC7600;
|
||||
}
|
||||
.kwd
|
||||
{
|
||||
color: #93C763;
|
||||
}
|
||||
.com
|
||||
{
|
||||
color: #66747B;
|
||||
}
|
||||
.typ
|
||||
{
|
||||
color: #678CB1;
|
||||
}
|
||||
.lit
|
||||
{
|
||||
color: #FACD22;
|
||||
}
|
||||
.pun
|
||||
{
|
||||
color: #F1F2F3;
|
||||
}
|
||||
.pln
|
||||
{
|
||||
color: #F1F2F3;
|
||||
}
|
||||
.tag
|
||||
{
|
||||
color: #8AC763;
|
||||
}
|
||||
.atn
|
||||
{
|
||||
color: #E0E2E4;
|
||||
}
|
||||
.atv
|
||||
{
|
||||
color: #EC7600;
|
||||
}
|
||||
.dec
|
||||
{
|
||||
color: purple;
|
||||
}
|
||||
pre.prettyprint
|
||||
{
|
||||
border: 0px solid #888;
|
||||
}
|
||||
ol.linenums
|
||||
{
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.prettyprint {
|
||||
background: #000;
|
||||
}
|
||||
li.L0, li.L1, li.L2, li.L3, li.L4, li.L5, li.L6, li.L7, li.L8, li.L9
|
||||
{
|
||||
color: #555;
|
||||
list-style-type: decimal;
|
||||
}
|
||||
li.L1, li.L3, li.L5, li.L7, li.L9 {
|
||||
background: #111;
|
||||
}
|
||||
@media print
|
||||
{
|
||||
.str
|
||||
{
|
||||
color: #060;
|
||||
}
|
||||
.kwd
|
||||
{
|
||||
color: #006;
|
||||
font-weight: bold;
|
||||
}
|
||||
.com
|
||||
{
|
||||
color: #600;
|
||||
font-style: italic;
|
||||
}
|
||||
.typ
|
||||
{
|
||||
color: #404;
|
||||
font-weight: bold;
|
||||
}
|
||||
.lit
|
||||
{
|
||||
color: #044;
|
||||
}
|
||||
.pun
|
||||
{
|
||||
color: #440;
|
||||
}
|
||||
.pln
|
||||
{
|
||||
color: #000;
|
||||
}
|
||||
.tag
|
||||
{
|
||||
color: #006;
|
||||
font-weight: bold;
|
||||
}
|
||||
.atn
|
||||
{
|
||||
color: #404;
|
||||
}
|
||||
.atv
|
||||
{
|
||||
color: #060;
|
||||
}
|
||||
}
|
BIN
zerobin/static/img/android-chrome-192x192.png
Executable file
After Width: | Height: | Size: 9.2 KiB |
BIN
zerobin/static/img/android-chrome-512x512.png
Executable file
After Width: | Height: | Size: 26 KiB |
BIN
zerobin/static/img/apple-touch-icon.png
Executable file
After Width: | Height: | Size: 8.3 KiB |
BIN
zerobin/static/img/binance1.png
Normal file
After Width: | Height: | Size: 261 KiB |
BIN
zerobin/static/img/binance2.png
Normal file
After Width: | Height: | Size: 580 KiB |
BIN
zerobin/static/img/binance3.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
zerobin/static/img/binance4.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
zerobin/static/img/binance5.png
Normal file
After Width: | Height: | Size: 147 KiB |
BIN
zerobin/static/img/binance6.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
zerobin/static/img/favicon-16x16.png
Executable file
After Width: | Height: | Size: 522 B |
BIN
zerobin/static/img/favicon-32x32.png
Executable file
After Width: | Height: | Size: 1.1 KiB |
BIN
zerobin/static/img/favicon.ico
Normal file → Executable file
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 15 KiB |
77
zerobin/static/js/additional.min.js
vendored
@ -15,45 +15,42 @@ Vue.options.delimiters = ['{%', '%}'];
|
||||
|
||||
// Force focus for textarea (firefox hack)
|
||||
setTimeout(function () {
|
||||
document.querySelector('textarea').focus()
|
||||
document.getElementById('content').focus();
|
||||
}, 100)
|
||||
|
||||
// Parse obfuscaded emails and make them usable
|
||||
const menu = new Vue({
|
||||
el: "#menu-top",
|
||||
methods: {
|
||||
formatEmail: (email) => {
|
||||
return "mailto:" + email.replace('__AT__', '@');
|
||||
},
|
||||
}
|
||||
})
|
||||
var app = new Vue({
|
||||
|
||||
const app = new Vue({
|
||||
|
||||
el: '#wrap-content',
|
||||
el: '#app',
|
||||
data: {
|
||||
previousPastes: [],
|
||||
downloadLink: {},
|
||||
displayBottomToolBar: false,
|
||||
openPreviousPastesMenu: false,
|
||||
readerMode: false,
|
||||
isUploading: false,
|
||||
btcCopied: false,
|
||||
currentPaste: {
|
||||
ownerKey: '',
|
||||
id: ''
|
||||
id: '',
|
||||
type: '',
|
||||
content: '',
|
||||
downloadLink: {},
|
||||
title: '',
|
||||
btcTipAddress: ''
|
||||
},
|
||||
newPaste: {
|
||||
expiration: '1_day',
|
||||
content: '',
|
||||
title: '',
|
||||
btcTipAddress: ''
|
||||
},
|
||||
messages: [],
|
||||
/** Check for browser support of the named featured. Store the result
|
||||
and add a class to the html tag with the result */
|
||||
support: {
|
||||
|
||||
clipboard: (function () {
|
||||
var val = !!(navigator.clipboard);
|
||||
document.querySelector('html').classList.add((val ? '' : 'no-') + 'clipboard');
|
||||
return val;
|
||||
})(),
|
||||
clipboard: !!(isSecureContext && navigator.clipboard && navigator.clipboard.writeText),
|
||||
|
||||
URLSearchParams: !!window.URLSearchParams,
|
||||
|
||||
localStorage: (function () {
|
||||
var val = !!(localStorage);
|
||||
@ -77,48 +74,92 @@ const app = new Vue({
|
||||
isLoading: false
|
||||
},
|
||||
methods: {
|
||||
forceLoadPaste: (link) => {
|
||||
|
||||
toggleReaderMode: function () {
|
||||
if (!this.readerMode) {
|
||||
this.messages = [];
|
||||
if (this.support.URLSearchParams) {
|
||||
var searchParams = new URLSearchParams(window.location.search);
|
||||
searchParams.set('readerMode', 1);
|
||||
window.location.search = searchParams.toString();
|
||||
}
|
||||
} else {
|
||||
if (this.support.URLSearchParams) {
|
||||
var searchParams = new URLSearchParams(window.location.search);
|
||||
searchParams.delete('readerMode');
|
||||
window.location.search = searchParams.toString();
|
||||
}
|
||||
}
|
||||
|
||||
this.readerMode = !this.readerMode;
|
||||
},
|
||||
|
||||
increaseFontSize: function (amount) {
|
||||
var readableModeContent = document.getElementById('readable-paste-content');
|
||||
|
||||
var fontSize = window.getComputedStyle(readableModeContent, null).getPropertyValue('font-size');
|
||||
|
||||
amount = amount || 5;
|
||||
readableModeContent.style.fontSize = (parseFloat(fontSize) + amount) + "px";
|
||||
},
|
||||
|
||||
decreaseFontSize: function () {
|
||||
this.increaseFontSize(-5);
|
||||
},
|
||||
|
||||
formatEmail: function (email) {
|
||||
return "mailto:" + email.replace('__AT__', '@');
|
||||
},
|
||||
|
||||
forceLoad: function (link) {
|
||||
window.location = link;
|
||||
window.location.reload();
|
||||
},
|
||||
|
||||
handleClone: () => {
|
||||
handleClone: function () {
|
||||
|
||||
document.querySelector('.submit-form').style.display = "inherit";
|
||||
document.querySelector('.paste-form').style.display = "none";
|
||||
let content = document.getElementById('content');
|
||||
var title = document.querySelector('h1');
|
||||
if (title) {
|
||||
title.style.display = "none";
|
||||
}
|
||||
var content = document.getElementById('content');
|
||||
content.value = zerobin.getPasteContent();
|
||||
content.dispatchEvent(new Event('change'));
|
||||
this.newPaste.title = this.currentPaste.title;
|
||||
this.newPaste.btcTipAddress = this.currentPaste.btcTipAddress;
|
||||
},
|
||||
|
||||
handleCancelClone: () => {
|
||||
handleCancelClone: function () {
|
||||
document.querySelector('.submit-form').style.display = "none";
|
||||
document.querySelector('.paste-form').style.display = "inherit";
|
||||
document.querySelector('h1').style.display = "inherit";
|
||||
},
|
||||
|
||||
handleUpload: (files) => {
|
||||
handleUpload: function (files) {
|
||||
try {
|
||||
app.isUploading = true;
|
||||
zerobin.upload(files);
|
||||
} catch (e) {
|
||||
zerobin.message('error', 'Could no upload the file', 'Error');
|
||||
zerobin.message('danger', 'Could no upload the file', 'Error');
|
||||
}
|
||||
app.isUploading = false;
|
||||
},
|
||||
|
||||
handleForceColoration: (e) => {
|
||||
let content = document.getElementById('paste-content');
|
||||
handleForceColoration: function (e) {
|
||||
var content = document.getElementById('paste-content');
|
||||
content.classList.add('linenums');
|
||||
e.target.innerHTML = 'Applying coloration';
|
||||
prettyPrint();
|
||||
e.target.parentNode.remove()
|
||||
},
|
||||
|
||||
handleSendByEmail: (e) => {
|
||||
e.target.href = 'mailto:friend@example.com?body=' + window.location.toString();
|
||||
handleSendByEmail: function (e) {
|
||||
window.location = 'mailto:friend@example.com?body=' + window.location.toString();
|
||||
},
|
||||
|
||||
handleDeletePaste: () => {
|
||||
handleDeletePaste: function () {
|
||||
if (window.confirm("Delete this paste?")) {
|
||||
app.isLoading = true;
|
||||
bar.set('Deleting paste...', '50%');
|
||||
@ -131,19 +172,20 @@ const app = new Vue({
|
||||
}).then(function (response) {
|
||||
if (response.ok) {
|
||||
window.location = "/";
|
||||
window.reload()
|
||||
} else {
|
||||
form.forEach((node) => node.disabled = false);
|
||||
form.forEach(function (node) {
|
||||
node.disabled = false;
|
||||
});
|
||||
app.isLoading = false
|
||||
zerobin.message(
|
||||
'error',
|
||||
'danger',
|
||||
'Paste could not be deleted. Please try again later.',
|
||||
'Error');
|
||||
}
|
||||
app.isLoading = false;
|
||||
}).catch(function (error) {
|
||||
zerobin.message(
|
||||
'error',
|
||||
'danger',
|
||||
'Paste could not be delete. Please try again later.',
|
||||
'Error');
|
||||
app.isLoading = false;
|
||||
@ -151,35 +193,78 @@ const app = new Vue({
|
||||
}
|
||||
},
|
||||
|
||||
copyToClipboard: () => {
|
||||
copyToClipboard: function () {
|
||||
|
||||
var pasteContent = zerobin.getPasteContent();
|
||||
let promise;
|
||||
|
||||
if (pasteContent.indexOf("data:image") === 0) {
|
||||
|
||||
promise = fetch(pasteContent).then((res) => {
|
||||
return res.blob().then(blob => {
|
||||
return navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
[blob.type]: blob
|
||||
})
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
} else {
|
||||
promise = navigator.clipboard.writeText(pasteContent);
|
||||
}
|
||||
var promise = navigator.clipboard.writeText(pasteContent);
|
||||
|
||||
promise.then(function () {
|
||||
zerobin.message('info', 'The paste is now in your clipboard', '', true);
|
||||
zerobin.message('primary', 'The paste is now in your clipboard', '', true);
|
||||
}, function (err) {
|
||||
zerobin.message('error', 'The paste could not be copied', '', true);
|
||||
zerobin.message('danger', 'The paste could not be copied', '', true);
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
copyBTCAdressToClipboard: function () {
|
||||
|
||||
var promise = navigator.clipboard.writeText(this.currentPaste.btcTipAddress);
|
||||
|
||||
app.btcCopied = true;
|
||||
|
||||
promise.then(function () {
|
||||
|
||||
if (app.btcCopied) {
|
||||
clearTimeout(app.btcCopied);
|
||||
}
|
||||
app.btcCopied = setTimeout(function () {
|
||||
app.btcCopied = false;
|
||||
}, 3000)
|
||||
}, function (err) {
|
||||
zerobin.message('danger', 'The BTC addresse could not be copied', '', true);
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
compressImage: function (base64) {
|
||||
var canvas = document.createElement('canvas')
|
||||
var img = document.createElement('img')
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
img.onload = function () {
|
||||
var width = img.width;
|
||||
var height = img.height;
|
||||
var biggest = width > height ? width : height;
|
||||
var maxHeight = height;
|
||||
var maxWidth = width;
|
||||
|
||||
if (width > height) {
|
||||
if (width > maxWidth) {
|
||||
height = Math.round((height *= maxWidth / width));
|
||||
width = maxWidth;
|
||||
}
|
||||
} else {
|
||||
if (height > maxHeight) {
|
||||
width = Math.round((width *= maxHeight / height));
|
||||
height = maxHeight;
|
||||
}
|
||||
}
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
|
||||
resolve(canvas.toDataURL('image/jpeg', 0.7));
|
||||
}
|
||||
img.onerror = function (err) {
|
||||
reject(err);
|
||||
}
|
||||
img.src = base64;
|
||||
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
On the create paste page:
|
||||
On click on the send button, compress and encrypt data before
|
||||
@ -187,7 +272,7 @@ const app = new Vue({
|
||||
newly created paste, adding the key in the hash.
|
||||
*/
|
||||
|
||||
encryptAndSendPaste: (e) => {
|
||||
encryptAndSendPaste: function () {
|
||||
|
||||
var paste = document.querySelector('textarea').value;
|
||||
|
||||
@ -195,7 +280,9 @@ const app = new Vue({
|
||||
|
||||
var form = document.querySelectorAll('input, textarea, select, button');
|
||||
|
||||
form.forEach((node) => node.disabled = true);
|
||||
form.forEach(function (node) {
|
||||
node.disabled = true;
|
||||
});
|
||||
|
||||
// set up progress bar
|
||||
var bar = zerobin.progressBar('form.well .progress');
|
||||
@ -210,76 +297,114 @@ const app = new Vue({
|
||||
|
||||
var key = zerobin.makeKey(256);
|
||||
|
||||
zerobin.encrypt(key, paste,
|
||||
var promise = new Promise(function (resolve, reject) {
|
||||
resolve(paste);
|
||||
}); // noop to avoid branching
|
||||
if (paste.indexOf('data:image') == 0) {
|
||||
promise = app.compressImage(paste);
|
||||
}
|
||||
|
||||
() => bar.set('Encoding to base64...', '45%'),
|
||||
() => bar.set('Compressing...', '65%'),
|
||||
() => bar.set('Encrypting...', '85%'),
|
||||
promise.then(function (base64) {
|
||||
zerobin.encrypt(key, base64,
|
||||
|
||||
/* This block deals with sending the data, redirection or error handling */
|
||||
function (content) {
|
||||
function () {
|
||||
bar.set('Encoding to base64...', '45%')
|
||||
},
|
||||
function () {
|
||||
bar.set('Compressing...', '65%')
|
||||
},
|
||||
function () {
|
||||
bar.set('Encrypting...', '85%')
|
||||
},
|
||||
|
||||
bar.set('Sending...', '95%');
|
||||
var data = {
|
||||
content: content,
|
||||
expiration: app.newPaste.expiration
|
||||
};
|
||||
var sizebytes = zerobin.count(JSON.stringify(data));
|
||||
var oversized = sizebytes > zerobin.max_size; // 100kb - the others header information
|
||||
var readableFsize = Math.round(sizebytes / 1024);
|
||||
var readableMaxsize = Math.round(zerobin.max_size / 1024);
|
||||
/* This block deals with sending the data, redirection or error handling */
|
||||
function (content) {
|
||||
|
||||
if (oversized) {
|
||||
app.isLoading = false
|
||||
form.forEach((node) => node.disabled = false);
|
||||
zerobin.message('error', ('The encrypted file was <strong class="file-size">' + readableFsize +
|
||||
'</strong>KB. You have reached the maximum size limit of ' + readableMaxsize + 'KB.'),
|
||||
'Warning!', true);
|
||||
return;
|
||||
}
|
||||
bar.set('Sending...', '95%');
|
||||
var data = {
|
||||
content: content,
|
||||
expiration: app.newPaste.expiration,
|
||||
title: app.newPaste.title,
|
||||
btcTipAddress: app.newPaste.btcTipAddress
|
||||
};
|
||||
var sizebytes = zerobin.count(JSON.stringify(data));
|
||||
var oversized = sizebytes > zerobin.max_size; // 100kb - the others header information
|
||||
var readableFsize = Math.round(sizebytes / 1024);
|
||||
var readableMaxsize = Math.round(zerobin.max_size / 1024);
|
||||
|
||||
fetch('/paste/create', {
|
||||
method: "POST",
|
||||
body: new URLSearchParams(data)
|
||||
}).then(function (response) {
|
||||
if (response.ok) {
|
||||
bar.set('Redirecting to new paste...', '100%');
|
||||
if (oversized) {
|
||||
app.isLoading = false
|
||||
form.forEach(function (node) {
|
||||
node.disabled = false;
|
||||
});
|
||||
|
||||
zerobin.message('danger', ('The file was <strong class="file-size">' + readableFsize +
|
||||
'</strong>KB after encryption. You have reached the maximum size limit of ' + readableMaxsize + 'KB.'),
|
||||
'Warning!', true);
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/paste/create', {
|
||||
method: "POST",
|
||||
body: new URLSearchParams(data)
|
||||
}).then(function (response) {
|
||||
if (response.ok) {
|
||||
bar.set('Redirecting to new paste...', '100%');
|
||||
|
||||
response.json().then(function (data) {
|
||||
if (data.status === 'error') {
|
||||
zerobin.message('danger', data.message, 'Error');
|
||||
form.forEach(function (node) {
|
||||
node.disabled = false;
|
||||
});
|
||||
app.isLoading = false;
|
||||
} else {
|
||||
if (app.support.localStorage) {
|
||||
zerobin.storePaste('/paste/' + data.paste + "?owner_key=" + data.owner_key + '#' + key);
|
||||
}
|
||||
window.location = ('/paste/' + data.paste + '#' + key);
|
||||
}
|
||||
})
|
||||
|
||||
response.json().then((data) => {
|
||||
if (data.status === 'error') {
|
||||
zerobin.message('error', data.message, 'Error');
|
||||
form.forEach((node) => node.disabled = false);
|
||||
app.isLoading = false
|
||||
} else {
|
||||
if (app.support.localStorage) {
|
||||
zerobin.storePaste('/paste/' + data.paste + "?owner_key=" + data.owner_key + '#' + key);
|
||||
}
|
||||
window.location = ('/paste/' + data.paste + '#' + key);
|
||||
form.forEach(function (node) {
|
||||
node.disabled = false
|
||||
});
|
||||
app.isLoading = false;
|
||||
zerobin.message(
|
||||
'danger',
|
||||
'Paste could not be saved. Please try again later.',
|
||||
'Error');
|
||||
}
|
||||
})
|
||||
}).catch(function (error) {
|
||||
form.forEach(function (node) {
|
||||
node.disabled = false;
|
||||
});
|
||||
app.isLoading = false;
|
||||
zerobin.message(
|
||||
'danger',
|
||||
'Paste could not be saved. Please try again later.',
|
||||
'Error');
|
||||
});
|
||||
|
||||
} else {
|
||||
form.forEach((node) => node.disabled = false);
|
||||
app.isLoading = false
|
||||
zerobin.message(
|
||||
'error',
|
||||
'Paste could not be saved. Please try again later.',
|
||||
'Error');
|
||||
}
|
||||
}).catch(function (error) {
|
||||
form.forEach((node) => node.disabled = false);
|
||||
app.isLoading = false
|
||||
zerobin.message(
|
||||
'error',
|
||||
'Paste could not be saved. Please try again later.',
|
||||
'Error');
|
||||
})
|
||||
}),
|
||||
function (err) {
|
||||
debugger;
|
||||
form.forEach(function (node) {
|
||||
node.disabled = false;
|
||||
});
|
||||
app.isLoading = false;
|
||||
zerobin.message('danger', 'Paste could not be encrypted. Aborting.',
|
||||
'Error');
|
||||
};
|
||||
|
||||
});
|
||||
} catch (err) {
|
||||
form.forEach((node) => node.disabled = false);
|
||||
app.isLoading = false
|
||||
zerobin.message('error', 'Paste could not be encrypted. Aborting.',
|
||||
form.forEach(function (node) {
|
||||
node.disabled = false;
|
||||
});
|
||||
app.isLoading = false;
|
||||
zerobin.message('danger', 'Paste could not be encrypted. Aborting.',
|
||||
'Error');
|
||||
}
|
||||
}
|
||||
@ -326,11 +451,13 @@ window.zerobin = {
|
||||
content = sjcl.encrypt(key, content);
|
||||
} catch (e) {
|
||||
|
||||
document.querySelectorAll('input, textarea, select, button').forEach((node) => node.disabled = true);
|
||||
document.querySelectorAll('input, textarea, select, button').forEach(function (node) {
|
||||
node.disabled = true
|
||||
});
|
||||
|
||||
app.isLoading = false;
|
||||
|
||||
zerobin.message('error', 'Paste could not be encrypted. Aborting.',
|
||||
zerobin.message('danger', 'Paste could not be encrypted. Aborting.',
|
||||
'Error');
|
||||
}
|
||||
if (doneCallback) {
|
||||
@ -367,7 +494,7 @@ window.zerobin = {
|
||||
/* Decompress */
|
||||
setTimeout(function () {
|
||||
try {
|
||||
content = lzw.decompress(content);
|
||||
|
||||
if (fromBase64Callback) {
|
||||
fromBase64Callback();
|
||||
}
|
||||
@ -388,6 +515,7 @@ window.zerobin = {
|
||||
doneCallback(content);
|
||||
}
|
||||
} catch (err) {
|
||||
debugger;
|
||||
errorCallback(err);
|
||||
}
|
||||
|
||||
@ -497,7 +625,7 @@ window.zerobin = {
|
||||
displayDate = pasteDateTime.split(' ')[1];
|
||||
prefix = 'at ';
|
||||
}
|
||||
let link = localStorage.getItem(key);
|
||||
var link = localStorage.getItem(key);
|
||||
|
||||
return {
|
||||
displayDate: displayDate,
|
||||
@ -548,10 +676,10 @@ window.zerobin = {
|
||||
/** Return the paste content stripted from any code coloration */
|
||||
getPasteContent: function () {
|
||||
var copy = '';
|
||||
document.querySelectorAll("#paste-content li").forEach((node) => {
|
||||
document.querySelectorAll("#paste-content li").forEach(function (node) {
|
||||
copy = copy + node.textContent.replace(/[\u00a0]+/g, ' ') + '\n';
|
||||
|
||||
})
|
||||
});
|
||||
if (copy === '') {
|
||||
copy = document.querySelector("#paste-content").textContent;
|
||||
}
|
||||
@ -576,7 +704,7 @@ window.zerobin = {
|
||||
message: function (type, message, title, flush, callback, action) {
|
||||
window.scrollTo(0, 0);
|
||||
if (flush) {
|
||||
app.messages = app.messages.filter((msg) => {
|
||||
app.messages = app.messages.filter(function (msg) {
|
||||
msg.type !== type
|
||||
});
|
||||
}
|
||||
@ -586,7 +714,7 @@ window.zerobin = {
|
||||
type: type,
|
||||
action: action || {},
|
||||
});
|
||||
callback && callback()
|
||||
callback && callback();
|
||||
},
|
||||
|
||||
/** Return a progress bar object */
|
||||
@ -632,19 +760,28 @@ window.zerobin = {
|
||||
return total * 1000 / size;
|
||||
},
|
||||
|
||||
// prevent defaults
|
||||
ignoreDrag: function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
},
|
||||
|
||||
// Handle Drop
|
||||
handleDrop: function (e) {
|
||||
e.preventDefault();
|
||||
zerobin.upload(e.dataTransfer.files);
|
||||
document.getElementById("content").classList.remove("hover");
|
||||
},
|
||||
|
||||
handlePaste: function (e) {
|
||||
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].type.indexOf("image") === 0) {
|
||||
e.preventDefault()
|
||||
return zerobin.upload([items[i].getAsFile()]);
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
handleDragOver: function (e) {
|
||||
zerobin.ignoreDrag(e);
|
||||
document.getElementById("content").classList.add('hover');
|
||||
@ -655,59 +792,36 @@ window.zerobin = {
|
||||
},
|
||||
|
||||
upload: function (files) {
|
||||
let content = document.getElementById('content');
|
||||
var current_file = files[0];
|
||||
var content = document.getElementById('content');
|
||||
var currentFile = files[0];
|
||||
var reader = new FileReader();
|
||||
if (current_file.type.indexOf('image') == 0) {
|
||||
if (currentFile.type.indexOf('image') == 0) {
|
||||
reader.onload = function (event) {
|
||||
var image = new Image();
|
||||
image.src = event.target.result;
|
||||
content.value = event.target.result
|
||||
|
||||
image.onload = function () {
|
||||
var maxWidth = 1024,
|
||||
maxHeight = 1024,
|
||||
imageWidth = image.width,
|
||||
imageHeight = image.height;
|
||||
|
||||
if (imageWidth > imageHeight) {
|
||||
if (imageWidth > maxWidth) {
|
||||
imageHeight *= maxWidth / imageWidth;
|
||||
imageWidth = maxWidth;
|
||||
}
|
||||
} else {
|
||||
if (imageHeight > maxHeight) {
|
||||
imageWidth *= maxHeight / imageHeight;
|
||||
imageHeight = maxHeight;
|
||||
}
|
||||
app.messages = []
|
||||
var previousImage = document.querySelector('.paste-wrapper');
|
||||
if (previousImage) {
|
||||
previousImage.remove();
|
||||
}
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = imageWidth;
|
||||
canvas.height = imageHeight;
|
||||
image.width = imageWidth;
|
||||
image.height = imageHeight;
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(this, 0, 0, imageWidth, imageHeight);
|
||||
|
||||
var paste = canvas.toDataURL(current_file.type);
|
||||
|
||||
content.value = paste;
|
||||
content.dispatchEvent(new Event('change'));
|
||||
|
||||
image.style.maxWidth = '742px';
|
||||
|
||||
var imgWrapper = document.createElement('div');
|
||||
imgWrapper.classList.add('paste-wrapper');
|
||||
imgWrapper.appendChild(image)
|
||||
content.style.display = "none";
|
||||
content.after(image);
|
||||
|
||||
content.after(imgWrapper);
|
||||
}
|
||||
}
|
||||
reader.readAsDataURL(current_file);
|
||||
reader.readAsDataURL(currentFile);
|
||||
} else {
|
||||
reader.onload = function (event) {
|
||||
content.value = event.target.result
|
||||
content.dispatchEvent(new Event('change'));
|
||||
};
|
||||
reader.readAsText(current_file);
|
||||
reader.readAsText(currentFile);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -719,8 +833,8 @@ window.zerobin = {
|
||||
Also calculate and set the paste visual hash.
|
||||
*/
|
||||
|
||||
let pasteContent = document.querySelector('#paste-content');
|
||||
let content = '';
|
||||
var pasteContent = document.querySelector('#paste-content');
|
||||
var content = '';
|
||||
|
||||
if (pasteContent) {
|
||||
content = pasteContent.textContent.trim();
|
||||
@ -733,7 +847,9 @@ var error = false;
|
||||
if (content && key) {
|
||||
|
||||
var form = document.querySelectorAll('input, textarea, select, button');
|
||||
form.forEach((node) => node.disabled = true);
|
||||
form.forEach(function (node) {
|
||||
node.disabled = true;
|
||||
});
|
||||
|
||||
var bar = zerobin.progressBar('.well form .progress');
|
||||
app.isLoading = true;
|
||||
@ -744,58 +860,84 @@ if (content && key) {
|
||||
/* On error*/
|
||||
function () {
|
||||
app.isLoading = false;
|
||||
zerobin.message('error', 'Could not decrypt data (Wrong key ?)', 'Error');
|
||||
zerobin.message('danger', 'Could not decrypt data (Wrong key ?)', 'Error');
|
||||
},
|
||||
|
||||
/* Update progress bar */
|
||||
() => bar.set('Decompressing...', '45%'),
|
||||
() => bar.set('Base64 decoding...', '65%'),
|
||||
() => bar.set('From bits to string...', '85%'),
|
||||
function () {
|
||||
bar.set('Decompressing...', '45%');
|
||||
},
|
||||
function () {
|
||||
bar.set('Base64 decoding...', '65%');
|
||||
},
|
||||
function () {
|
||||
bar.set('From bits to string...', '85%');
|
||||
},
|
||||
|
||||
/* When done */
|
||||
function (content) {
|
||||
|
||||
/* Decrypted content goes back to initial container*/
|
||||
document.querySelector('#paste-content').innerHTML = content;
|
||||
var readerMode = false;
|
||||
|
||||
if (content.indexOf('data:image') == 0) {
|
||||
// Display Image
|
||||
|
||||
let pasteContent = document.querySelector('#paste-content');
|
||||
app.currentPaste.type = "image";
|
||||
var pasteContent = document.querySelector('#paste-content');
|
||||
pasteContent.style.display = "none";
|
||||
|
||||
var img = document.createElement('img')
|
||||
var imgWrapper = document.createElement('div');
|
||||
imgWrapper.classList.add('paste-wrapper');
|
||||
var img = document.createElement('img');
|
||||
img.src = content;
|
||||
img.style.maxWidth = '742px';
|
||||
|
||||
pasteContent.after(img);
|
||||
pasteContent.after(imgWrapper);
|
||||
imgWrapper.appendChild(img);
|
||||
|
||||
// Display Download button
|
||||
document.querySelectorAll('.btn-clone').forEach((node) => node.style.display = "none")
|
||||
document.querySelectorAll('.btn-clone').forEach(function (node) {
|
||||
node.style.display = "none";
|
||||
});
|
||||
|
||||
app.downloadLink = {
|
||||
name: '0bin_' + document.location.pathname.split('/').pop(),
|
||||
var extension = /data:image\/([^;]+);base64/.exec(content)[1];
|
||||
|
||||
app.currentPaste.downloadLink = {
|
||||
name: '0bin_' + document.location.pathname.split('/').pop() + '.' + extension,
|
||||
url: content
|
||||
}
|
||||
|
||||
} else {
|
||||
app.currentPaste.type = "text";
|
||||
/* Decrypted content goes back to initial container*/
|
||||
document.querySelector('#paste-content').innerText = content;
|
||||
app.currentPaste.content = content;
|
||||
|
||||
app.currentPaste.downloadLink = {
|
||||
name: '0bin_' + document.location.pathname.split('/').pop() + ".txt",
|
||||
url: "data:text/html;charset=UTF-8," + content
|
||||
}
|
||||
|
||||
if (app.support.URLSearchParams) {
|
||||
readerMode = (new URLSearchParams(window.location.search)).get('readerMode');
|
||||
}
|
||||
|
||||
}
|
||||
bar.set('Code coloration...', '95%');
|
||||
|
||||
/* Add a continuation to let the UI redraw */
|
||||
/* Add a continuation to var the UI redraw */
|
||||
setTimeout(function () {
|
||||
|
||||
/** Syntaxic coloration */
|
||||
|
||||
if (zerobin.isCode(content) > 100) {
|
||||
if (zerobin.isCode(content) > 100 && !readerMode) {
|
||||
document.getElementById('paste-content').classList.add('linenums');
|
||||
prettyPrint();
|
||||
} else {
|
||||
if (content.indexOf('data:image') != 0) {
|
||||
zerobin.message('info',
|
||||
zerobin.message('primary',
|
||||
"The paste did not seem to be code, so it " +
|
||||
"was not colorized. ",
|
||||
'', false, undefined, {
|
||||
message: "Force coloration",
|
||||
message: "Click here to force coloration",
|
||||
callback: app.handleForceColoration
|
||||
});
|
||||
}
|
||||
@ -808,8 +950,13 @@ if (content && key) {
|
||||
bar.set('Done', '100%');
|
||||
app.isLoading = false;
|
||||
|
||||
form.forEach((node) => node.disabled = false);
|
||||
form.forEach(function (node) {
|
||||
node.disabled = false;
|
||||
});
|
||||
content = '';
|
||||
if (readerMode) {
|
||||
app.toggleReaderMode();
|
||||
}
|
||||
|
||||
}, 100);
|
||||
|
||||
@ -817,16 +964,40 @@ if (content && key) {
|
||||
|
||||
} /* End of "DECRYPTION" */
|
||||
|
||||
/* Display bottom paste option buttons when needed */
|
||||
|
||||
window.onload = function () {
|
||||
["keyup", "change"].forEach((event) => {
|
||||
let content = document.getElementById("content");
|
||||
content.addEventListener(event, () => {
|
||||
let height = parseFloat(getComputedStyle(content, null).height.replace("px", ""))
|
||||
|
||||
/* Display bottom paste option buttons when needed */
|
||||
["keyup", "change"].forEach(function (event) {
|
||||
var content = document.getElementById("content");
|
||||
content.addEventListener(event, function () {
|
||||
var height = parseFloat(getComputedStyle(content, null).height.replace("px", ""))
|
||||
app.displayBottomToolBar = height > 400;
|
||||
})
|
||||
})
|
||||
|
||||
/* Remove expired pasted from history **/
|
||||
if (app.support.history && app.currentPaste.type === 'not_found') {
|
||||
|
||||
var paste_id = zerobin.getPasteId();
|
||||
var keys = zerobin.getLocalStorageURLKeys();
|
||||
keys.forEach(function (key, i) {
|
||||
if (localStorage[key].indexOf(paste_id) !== -1) {
|
||||
localStorage.removeItem(key);
|
||||
return false;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var title = document.querySelector('h1');
|
||||
if (title) {
|
||||
app.currentPaste.title = title.innerText;
|
||||
}
|
||||
|
||||
var btcTipAddress = document.querySelector('.btc-tip-address');
|
||||
if (btcTipAddress) {
|
||||
app.currentPaste.btcTipAddress = btcTipAddress.innerText;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Display previous pastes */
|
||||
@ -839,21 +1010,22 @@ if (app.support.localStorage) {
|
||||
if (app.support.fileUpload) {
|
||||
|
||||
// Implements drag & drop upload
|
||||
let content = document.getElementById('content');
|
||||
var content = document.getElementById('content');
|
||||
content.addEventListener('drop', zerobin.handleDrop);
|
||||
content.addEventListener('paste', zerobin.handlePaste);
|
||||
content.addEventListener('dragover', zerobin.handleDragOver);
|
||||
content.addEventListener('dragleave', zerobin.handleDragLeave);
|
||||
|
||||
}
|
||||
|
||||
/* Remove expired pasted from history */
|
||||
if (app.support.history && zerobin.paste_not_found) {
|
||||
var paste_id = zerobin.getPasteId();
|
||||
var keys = zerobin.getLocalStorageURLKeys();
|
||||
keys.forEach((key, i) => {
|
||||
if (localStorage[key].indexOf(paste_id) !== -1) {
|
||||
localStorage.removeItem(key);
|
||||
return false;
|
||||
}
|
||||
})
|
||||
/* Autofit text area height */
|
||||
var tx = document.getElementsByTagName('textarea');
|
||||
for (var i = 0; i < tx.length; i++) {
|
||||
tx[i].setAttribute('style', 'height:' + (tx[i].scrollHeight) + 'px;overflow-y:hidden;');
|
||||
tx[i].addEventListener("input", OnInput, false);
|
||||
}
|
||||
|
||||
function OnInput() {
|
||||
this.style.height = 'auto';
|
||||
this.style.height = (this.scrollHeight) + 'px';
|
||||
}
|
||||
|
@ -1,110 +0,0 @@
|
||||
// Author: Anthony McKale
|
||||
//
|
||||
// Note: modifed to javascript from original as2 found below
|
||||
// basically identical actual to as2
|
||||
//
|
||||
// http://www.razorberry.com/blog/archives/2004/08/22/lzw-compression-methods-in-as2/
|
||||
//
|
||||
// A class for LZW compression modified from code posted at the following URL's
|
||||
// http://www.shoe-box.org/blog/index.php/2004/05/05/13-CompressionLzw
|
||||
// http://www.lalex.com/blog/comments/200405/164-compression-lzw-actionscript-2.html
|
||||
//
|
||||
var lzw = {
|
||||
// Change this variable to output an xml safe string
|
||||
xmlsafe : false,
|
||||
compress : function(str){
|
||||
var dico = new Array();
|
||||
var skipnum = lzw.xmlsafe?5:0;
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
dico[String.fromCharCode(i)] = i;
|
||||
}
|
||||
if (lzw.xmlsafe)
|
||||
{
|
||||
dico["<"] = 256;
|
||||
dico[">"] = 257;
|
||||
dico["&"] = 258;
|
||||
dico["\""] = 259;
|
||||
dico["'"] = 260;
|
||||
}
|
||||
var res = "";
|
||||
var txt2encode = str;
|
||||
var splitStr = txt2encode.split("");
|
||||
var len = splitStr.length;
|
||||
var nbChar = 256+skipnum;
|
||||
var buffer = "";
|
||||
for (var i = 0; i <= len; i++)
|
||||
{
|
||||
var current = splitStr[i];
|
||||
if (dico[buffer + current] !== undefined)
|
||||
{
|
||||
buffer += current;
|
||||
}
|
||||
else
|
||||
{
|
||||
res += String.fromCharCode(dico[buffer]);
|
||||
dico[buffer + current] = nbChar;
|
||||
nbChar++;
|
||||
buffer = current;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
},
|
||||
decompress : function (str)
|
||||
{
|
||||
var dico = new Array();
|
||||
var skipnum = lzw.xmlsafe?5:0;
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
var c = String.fromCharCode(i);
|
||||
dico[i] = c;
|
||||
}
|
||||
if (lzw.xmlsafe)
|
||||
{
|
||||
dico[256] = "<";
|
||||
dico[257] = ">";
|
||||
dico[258] = "&";
|
||||
dico[259] = "\"";
|
||||
dico[260] = "'";
|
||||
}
|
||||
var txt2encode = str;
|
||||
var splitStr = txt2encode.split("");
|
||||
var length = splitStr.length;
|
||||
var nbChar = 256+skipnum;
|
||||
var buffer = "";
|
||||
var chaine = "";
|
||||
var result = "";
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var code = txt2encode.charCodeAt(i);
|
||||
var current = dico[code];
|
||||
if (buffer == "")
|
||||
{
|
||||
buffer = current;
|
||||
result += current;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (code <= 255+skipnum)
|
||||
{
|
||||
result += current;
|
||||
chaine = buffer + current;
|
||||
dico[nbChar] = chaine;
|
||||
nbChar++;
|
||||
buffer = current;
|
||||
}
|
||||
else
|
||||
{
|
||||
chaine = dico[code];
|
||||
if (chaine == undefined) chaine = buffer + buffer.slice(0,1);
|
||||
result += chaine;
|
||||
dico[nbChar] = buffer + chaine.slice(0, 1);
|
||||
nbChar++;
|
||||
buffer = chaine;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
57
zerobin/static/js/main.min.js
vendored
121
zerobin/utils.py
@ -1,47 +1,19 @@
|
||||
import time
|
||||
import os
|
||||
import glob
|
||||
import tempfile
|
||||
import codecs
|
||||
import unicodedata
|
||||
import hashlib
|
||||
import secrets
|
||||
from functools import partial
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import bottle
|
||||
|
||||
from appdirs import AppDirs
|
||||
|
||||
import zerobin
|
||||
from zerobin import default_settings
|
||||
|
||||
try:
|
||||
from zerobin.privilege import drop_privileges_permanently, coerce_user, coerce_group
|
||||
except (AttributeError):
|
||||
pass # privilege does't work on several plateforms
|
||||
|
||||
|
||||
from runpy import run_path
|
||||
|
||||
|
||||
def drop_privileges(user=None, group=None, wait=5):
|
||||
"""
|
||||
Try to set the process user and group to another one.
|
||||
If no group is provided, it's set to the same as the user.
|
||||
You can wait for a certain time before doing so.
|
||||
"""
|
||||
if wait:
|
||||
time.sleep(wait)
|
||||
if user:
|
||||
group = group or user
|
||||
try:
|
||||
user = coerce_user(user)
|
||||
group = coerce_group(group)
|
||||
|
||||
lock_files = glob.glob(os.path.join(tempfile.gettempdir(), "bottle.*.lock"))
|
||||
for lock_file in lock_files:
|
||||
os.chown(lock_file, user, group)
|
||||
|
||||
drop_privileges_permanently(user, group, ())
|
||||
except Exception:
|
||||
print("Failed to drop privileges. Running with current user.")
|
||||
|
||||
|
||||
class SettingsValidationError(Exception):
|
||||
pass
|
||||
|
||||
@ -60,12 +32,12 @@ class SettingsContainer(object):
|
||||
cls._instance.update_with_module(default_settings)
|
||||
return cls._instance
|
||||
|
||||
def update_with_dict(self, dict):
|
||||
def update_with_dict(self, mapping):
|
||||
"""
|
||||
Update settings with values from the given mapping object.
|
||||
(Taking only variable with uppercased name)
|
||||
"""
|
||||
for name, value in dict.items():
|
||||
for name, value in mapping.items():
|
||||
if name.isupper():
|
||||
setattr(self, name, value)
|
||||
return self
|
||||
@ -83,44 +55,23 @@ class SettingsContainer(object):
|
||||
Create an instance of SettingsContainer with values based
|
||||
on the one in the passed module.
|
||||
"""
|
||||
settings = cls()
|
||||
settings.update_with_module(module)
|
||||
return settings
|
||||
params = cls()
|
||||
params.update_with_module(module)
|
||||
return params
|
||||
|
||||
def update_with_file(self, filepath):
|
||||
"""
|
||||
Update settings with values from the given module file.
|
||||
Uses update_with_dict() behind the scenes.
|
||||
"""
|
||||
settings = run_path(filepath)
|
||||
return self.update_with_dict(settings)
|
||||
params = run_path(filepath)
|
||||
return self.update_with_dict(params)
|
||||
|
||||
|
||||
settings = SettingsContainer()
|
||||
|
||||
|
||||
def to_ascii(utext):
|
||||
""" Take a unicode string and return ascii bytes.
|
||||
|
||||
Try to replace non ASCII char by similar ASCII char. If it can't,
|
||||
replace it with "?".
|
||||
"""
|
||||
return unicodedata.normalize("NFKD", utext).encode("ascii", "replace")
|
||||
|
||||
|
||||
# Make sure to always specify encoding when using open in Python 2 or 3
|
||||
safe_open = partial(codecs.open, encoding="utf8")
|
||||
|
||||
|
||||
def as_unicode(obj):
|
||||
""" Return the unicode representation of an object """
|
||||
try:
|
||||
return unicode(obj)
|
||||
except NameError:
|
||||
return str(obj)
|
||||
|
||||
|
||||
def ensure_var_env():
|
||||
def ensure_app_context(data_dir=None, config_dir=None):
|
||||
""" Ensure all the variable things we generate are available.
|
||||
|
||||
This will make sure we have:
|
||||
@ -129,20 +80,41 @@ def ensure_var_env():
|
||||
- a content dir
|
||||
- a secret key
|
||||
- an admin URL
|
||||
|
||||
This function is idempotent if nothing touch the files it created.
|
||||
"""
|
||||
|
||||
settings.VAR_DIR.mkdir(exist_ok=True, parents=True)
|
||||
settings.PASTE_FILES_ROOT = VAR_DIR / "content"
|
||||
app_dirs = AppDirs("0bin", "tygs")
|
||||
|
||||
settings.DATA_DIR = Path(data_dir or app_dirs.user_data_dir).expanduser()
|
||||
settings.DATA_DIR.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
settings.CONFIG_DIR = Path(config_dir or app_dirs.user_config_dir).expanduser()
|
||||
settings.CONFIG_DIR.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
settings.STATIC_FILES_ROOT = zerobin.ROOT_DIR / "static"
|
||||
|
||||
settings.PASTE_FILES_ROOT = settings.DATA_DIR / "pastes"
|
||||
settings.PASTE_FILES_ROOT.mkdir(exist_ok=True)
|
||||
settings.SESSIONS_DIR = VAR_DIR / "sessions"
|
||||
|
||||
settings.SESSIONS_DIR = settings.DATA_DIR / "sessions"
|
||||
settings.SESSIONS_DIR.mkdir(exist_ok=True)
|
||||
|
||||
secret_key_file = settings.VAR_DIR / "secret_key"
|
||||
bottle.TEMPLATE_PATH.insert(0, zerobin.ROOT_DIR / "views")
|
||||
|
||||
CUSTOM_VIEWS_DIR = settings.CONFIG_DIR / "custom_views"
|
||||
CUSTOM_VIEWS_DIR.mkdir(exist_ok=True)
|
||||
|
||||
bottle.TEMPLATE_PATH.insert(0, CUSTOM_VIEWS_DIR)
|
||||
|
||||
bottle.BaseRequest.MEMFILE_MAX = settings.MAX_SIZE + (1024 * 100)
|
||||
|
||||
secret_key_file = settings.CONFIG_DIR / "secret_key"
|
||||
if not secret_key_file.is_file():
|
||||
secret_key_file.write_text(secrets.token_urlsafe(64))
|
||||
settings.SECRET_KEY = secret_key_file.read_text()
|
||||
|
||||
admin_password_file = settings.VAR_DIR / "admin_password"
|
||||
admin_password_file = settings.CONFIG_DIR / "admin_password"
|
||||
if not secret_key_file.is_file():
|
||||
admin_password_file.write_text(
|
||||
"No password set. Use the set_admin_passord command. Don't write this file by hand."
|
||||
@ -150,7 +122,14 @@ def ensure_var_env():
|
||||
settings.ADMIN_PASSWORD_FILE = admin_password_file
|
||||
|
||||
payload = ("admin" + settings.SECRET_KEY).encode("ascii")
|
||||
settings.ADMIN_URL = "/admin/" + hashlib.sha256(payload).hexdigest()
|
||||
settings.ADMIN_URL = "/admin/" + hashlib.sha256(payload).hexdigest() + "/"
|
||||
|
||||
settings_file = settings.CONFIG_DIR / "settings.py"
|
||||
if not settings_file.is_file():
|
||||
default_config = (zerobin.ROOT_DIR / "default_settings.py").read_text()
|
||||
settings_file.write_text(default_config)
|
||||
|
||||
settings.update_with_file(settings_file)
|
||||
|
||||
|
||||
def hash_password(password):
|
||||
@ -166,7 +145,7 @@ def hash_password(password):
|
||||
|
||||
def check_password(password):
|
||||
try:
|
||||
return settings.ADMIN_PASSWORD_FILE.read_bytes() != hash_password(password)
|
||||
return settings.ADMIN_PASSWORD_FILE.read_bytes() == hash_password(password)
|
||||
except (FileNotFoundError, AttributeError):
|
||||
return False
|
||||
|
||||
|
@ -1,8 +1,4 @@
|
||||
<script type="text/javascript">
|
||||
zerobin.paste_not_found = true;
|
||||
</script>
|
||||
|
||||
<p class="alert alert-error">
|
||||
<p class="alert alert-warning alert-dismissible" :dummy="currentPaste.type = 'not_found'">
|
||||
<a class="close" data-dismiss="alert" href="#" @click.prevent="$event.target.parentNode.remove()">×</a>
|
||||
<strong class="title">404 Error!</strong>
|
||||
<span class="message">
|
||||
@ -10,41 +6,58 @@
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p class="file-upload">
|
||||
<input type="button" class="btn btn-upload" :value="isUploading ? 'Uploading...': 'Upload file'"
|
||||
:disabled="isUploading">
|
||||
<input type="file" class="hide-upload" id="file-upload" @change="handleUpload($event.target.files)">
|
||||
</p>
|
||||
|
||||
<form class="well" method="post" action="/paste/create" @submit.prevent="encryptAndSendPaste()">
|
||||
<p class=" paste-option">
|
||||
<label for="expiration">Expiration:</label>
|
||||
<select id="expiration" name="expiration" v-model="newPaste.expiration">
|
||||
<option value="burn_after_reading">Burn after reading</option>
|
||||
<option selected value="1_day">1 day</option>
|
||||
<option value="1_month">1 month</option>
|
||||
<option value="never">Never</option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-primary" ">Submit</button>
|
||||
</p>
|
||||
<p>
|
||||
<div class=" progress progress-striped active" v-show="isLoading">
|
||||
<div class="bar"></div>
|
||||
<div class="d-flex justify-content-between">
|
||||
|
||||
<div>
|
||||
<div class="file-upload" v-if="support.fileUpload">
|
||||
<input type="button" class="btn btn-primary" :value="isUploading ? 'Uploading...': 'Upload file'"
|
||||
:disabled="isUploading">
|
||||
<input type="file" class="hide-upload" id="file-upload" @change="handleUpload($event.target.files)">
|
||||
</div>
|
||||
<textarea rows="10" style="width:100%;" class="input-xlarge" id="content" name="content" autofocus
|
||||
v-on:keydown.prevent.ctrl.enter="encryptAndSendPaste()"></textarea>
|
||||
</p>
|
||||
<p class="paste-option down" v-if="displayBottomToolBar">
|
||||
<label for="expiration">Expiration:</label>
|
||||
<select id="expiration" name="expiration" v-model="newPaste.expiration">
|
||||
<option value="burn_after_reading">Burn after reading</option>
|
||||
<option selected value="1_day">1 day</option>
|
||||
<option value="1_month">1 month</option>
|
||||
<option value="never">Never</option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group select-date paste-option">
|
||||
<div class="input-group">
|
||||
<select id="expiration" name="expiration" class="custom-select" v-model="newPaste.expiration">
|
||||
<option value="burn_after_reading">Burn after reading</option>
|
||||
<option selected value="1_day">Expire in 1 day</option>
|
||||
<option value="1_month">Expire in 1 month</option>
|
||||
<option value="never">Never expire</option>
|
||||
</select>
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="pre-wrapper">
|
||||
<div class="progress" v-show="isLoading">
|
||||
<div class="progress-bar progress-bar-striped" role="progressbar"></div>
|
||||
</div>
|
||||
<textarea rows="10" style="width:100%;" class="form-control" id="content" name="content" autofocus
|
||||
@keydown.ctrl.enter="encryptAndSendPaste()"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group select-date paste-option down" v-if="displayBottomToolBar">
|
||||
<div class="input-group">
|
||||
<select id="expiration" name="expiration" class="custom-select" v-model="newPaste.expiration">
|
||||
<option value="burn_after_reading">Burn after reading</option>
|
||||
<option selected value="1_day">Expire in 1 day</option>
|
||||
<option value="1_month">Expire in 1 month</option>
|
||||
<option value="never">Never expire</option>
|
||||
</select>
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
|
||||
% rebase('base', settings=settings, pastes_count=pastes_count)
|
||||
|
@ -1,32 +1,45 @@
|
||||
%if is_authenticated:
|
||||
|
||||
<form action="" method="delete">
|
||||
<form action="./logout/" method="post">
|
||||
<div class="float-right logout">
|
||||
<button type="submit" class="btn btn-secondary">
|
||||
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-power" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M5.578 4.437a5 5 0 1 0 4.922.044l.5-.866a6 6 0 1 1-5.908-.053l.486.875z"/>
|
||||
<path fill-rule="evenodd" d="M7.5 8V1h1v7h-1z"/>
|
||||
</svg>
|
||||
Logout</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div>
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label>Paste to delete</label>
|
||||
<input type="text" class="form-control" placeholder="Paste URL or ID">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-black">Delete</button>
|
||||
</form>
|
||||
<div class="admin-header">
|
||||
<h3>Admin Panel</h3>
|
||||
<br>
|
||||
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-hammer" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.812 1.952a.5.5 0 0 1-.312.89c-1.671 0-2.852.596-3.616 1.185L4.857 5.073V6.21a.5.5 0 0 1-.146.354L3.425 7.853a.5.5 0 0 1-.708 0L.146 5.274a.5.5 0 0 1 0-.706l1.286-1.29a.5.5 0 0 1 .354-.146H2.84C4.505 1.228 6.216.862 7.557 1.04a5.009 5.009 0 0 1 2.077.782l.178.129z"/>
|
||||
<path fill-rule="evenodd" d="M6.012 3.5a.5.5 0 0 1 .359.165l9.146 8.646A.5.5 0 0 1 15.5 13L14 14.5a.5.5 0 0 1-.756-.056L4.598 5.297a.5.5 0 0 1 .048-.65l1-1a.5.5 0 0 1 .366-.147z"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
%if status == "error":
|
||||
<div class="alert alert-danger" role="alert alert-danger">
|
||||
{{message}}
|
||||
</div>
|
||||
%end
|
||||
%if status == "ok" and message:
|
||||
<div class="alert alert-success" role="alert">
|
||||
{{message}}
|
||||
</div>
|
||||
%end
|
||||
|
||||
<form action="." method="post">
|
||||
|
||||
<label>Paste to delete:</label>
|
||||
<div class="input-group">
|
||||
<input name="paste" type="text" class="form-control" placeholder="Paste URL or ID here">
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-danger">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
%else:
|
||||
<form action="/login" method="post">
|
||||
|
||||
<div class="login-form">
|
||||
<form>
|
||||
|
||||
<label>Password</label>
|
||||
<input type="password" class="form-control" placeholder="Password">
|
||||
<button type="submit" class="btn btn-black">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
%end
|
||||
|
||||
|
||||
% rebase('base', settings=settings, pastes_count=pastes_count)
|
||||
% rebase('base', settings=settings, pastes_count=pastes_count)
|
||||
|
@ -4,176 +4,144 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>0bin - encrypted pastebin</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
|
||||
<meta name="description" content="0bin is a client-side-encrypted
|
||||
pastebin featuring burn after reading, history, and
|
||||
a clipboard">
|
||||
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<link rel="icon" href="/static/img/favicon.ico" />
|
||||
<link rel="apple-touch-icon" href="/static/img/apple-touch-icon.png" />
|
||||
|
||||
%if settings.COMPRESSED_STATIC_FILES:
|
||||
%if not settings.DEBUG:
|
||||
<link href="/static/css/style.min.css?{{ VERSION }}" rel="stylesheet" />
|
||||
%else:
|
||||
<link href="/static/css/prettify.css" rel="stylesheet" />
|
||||
<link href="/static/css/bootstrap.css" rel="stylesheet">
|
||||
<link href="/static/css/desert.css" rel="stylesheet" />
|
||||
<link href="/static/css/bootswatch.4.5.css" rel="stylesheet">
|
||||
<link href="/static/css/style.css?{{ VERSION }}" rel="stylesheet">
|
||||
%end
|
||||
|
||||
<!-- Le HTML5 shim, for IE7-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="navbar navbar-fixed-top" id="menu-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</a>
|
||||
<a class="brand" href="/"><span>ø</span>bin<em>.net</em></a>
|
||||
<div class="nav-collapse">
|
||||
<ul class="nav">
|
||||
<div id="app" :class="{ 'reader-mode-bg': readerMode}">
|
||||
|
||||
%for i, entry in enumerate(settings.MENU):
|
||||
<li>
|
||||
%if "mailto:" in entry[1]:
|
||||
<a :href="formatEmail('{{ entry[1].replace('mailto:', '').replace('@', '__AT__') }}')" class="email-link">
|
||||
{{ entry[0] }}
|
||||
</a>
|
||||
%else:
|
||||
<a href="{{ entry[1] }}">{{ entry[0] }}</a>
|
||||
%end
|
||||
<div :class="{'topnav': true, 'reader-mode': readerMode}" @mouseleave="openPreviousPastesMenu =false">
|
||||
<a class="brand" href="/"><span>ø</span>bin<em>.net</em></a>
|
||||
<span class="tagline">"A client side encrypted PasteBin"<br><span>All pastes are AES256 encrypted, we cannot know
|
||||
what you paste...</span>
|
||||
</span>
|
||||
|
||||
</li>
|
||||
%end
|
||||
<nav>
|
||||
<ul>
|
||||
<li class="reader-tools min" v-if="readerMode">
|
||||
<a href="#" @click.prevent="decreaseFontSize()">
|
||||
<svg height="30" width="30">
|
||||
<text x="10" y="20" fill="#eee">A</text>
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
<li class="reader-tools max" v-if="readerMode">
|
||||
<a href="#" @click.prevent="increaseFontSize()">
|
||||
<svg height="30" width="30">
|
||||
<text x="10" y="20" fill="#eee">A</text>
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="reader-book" href="#" v-if="currentPaste.type === 'text'" @click.prevent="toggleReaderMode()">
|
||||
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-book" fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd"
|
||||
d="M3.214 1.072C4.813.752 6.916.71 8.354 2.146A.5.5 0 0 1 8.5 2.5v11a.5.5 0 0 1-.854.354c-.843-.844-2.115-1.059-3.47-.92-1.344.14-2.66.617-3.452 1.013A.5.5 0 0 1 0 13.5v-11a.5.5 0 0 1 .276-.447L.5 2.5l-.224-.447.002-.001.004-.002.013-.006a5.017 5.017 0 0 1 .22-.103 12.958 12.958 0 0 1 2.7-.869zM1 2.82v9.908c.846-.343 1.944-.672 3.074-.788 1.143-.118 2.387-.023 3.426.56V2.718c-1.063-.929-2.631-.956-4.09-.664A11.958 11.958 0 0 0 1 2.82z" />
|
||||
<path fill-rule="evenodd"
|
||||
d="M12.786 1.072C11.188.752 9.084.71 7.646 2.146A.5.5 0 0 0 7.5 2.5v11a.5.5 0 0 0 .854.354c.843-.844 2.115-1.059 3.47-.92 1.344.14 2.66.617 3.452 1.013A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.276-.447L15.5 2.5l.224-.447-.002-.001-.004-.002-.013-.006-.047-.023a12.582 12.582 0 0 0-.799-.34 12.96 12.96 0 0 0-2.073-.609zM15 2.82v9.908c-.846-.343-1.944-.672-3.074-.788-1.143-.118-2.387-.023-3.426.56V2.718c1.063-.929 2.631-.956 4.09-.664A11.956 11.956 0 0 1 15 2.82z" />
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
<li class="submenu"><a href="#" @click.prevent="openPreviousPastesMenu = !openPreviousPastesMenu">Previous
|
||||
pastes <span class="caret"></span></a>
|
||||
|
||||
<ul class="previous-pastes" id="topmenu" v-if="openPreviousPastesMenu">
|
||||
<li class="item" v-if="previousPastes.length === 0">
|
||||
<a href="#">No paste yet</a>
|
||||
</li>
|
||||
<li :class="{item: true, active: paste.isCurrent}" v-for="paste in previousPastes">
|
||||
<a :href="paste.link" @click="forceLoad(paste.link)">{% paste.displayDate %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
</ul>
|
||||
<p class="about pull-right">
|
||||
"A client side encrypted PasteBin"<br>
|
||||
<span>All pastes are AES256 encrypted, we cannot know what you paste...</span>
|
||||
</p>
|
||||
</div>
|
||||
<!--/.nav-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<noscript class="noscript">
|
||||
|
||||
<div class="container jumbotron">
|
||||
<h1 class="display-4">This site requires Javascript</h1>
|
||||
<p class="lead">This pastebin uses client-side encryption, and therefore, it needs JavaScript to work.</p>
|
||||
<p>It seems like your browser doesn't have JavaScript enabled.</p>
|
||||
<noscript class="container noscript">
|
||||
<p>This pastebin uses client-side encryption. Therefore, it needs JavaScript enabled.</p>
|
||||
<p>It seems like your browser doesn't have JavaScript enable.</p>
|
||||
<p>Please enable JavaScript for this website or use a JavaScript-capable web browser.</p>
|
||||
</div>
|
||||
</noscript>
|
||||
|
||||
</noscript>
|
||||
<div class="container-md" id="wrap-content">
|
||||
|
||||
<div class="container app" id="wrap-content" v-cloak>
|
||||
<div class="row">
|
||||
<div class="span2">
|
||||
<div class="well sidebar-nav">
|
||||
<ul class="nav nav-list previous-pastes">
|
||||
<li class="nav-header">Previous pastes</li>
|
||||
<li class="item local-storage" v-if="previousPastes.length === 0">
|
||||
<em class="grey">
|
||||
Your previous pastes will be saved in your browser using
|
||||
<a href="http://www.w3.org/TR/webstorage/">localStorage</a>.
|
||||
</em>
|
||||
</li>
|
||||
<li class="item no-local-storage">
|
||||
<em class="grey">
|
||||
Sorry your browser does not support
|
||||
<a href="http://www.w3.org/TR/webstorage/">LocalStorage</a>,
|
||||
We cannot display your previous pastes.
|
||||
</em>
|
||||
</li>
|
||||
<li :class="{item: true, active: paste.isCurrent}" v-for="paste in previousPastes">
|
||||
<a :href="paste.link" @click="forceLoadPaste(paste.link)">
|
||||
{% paste.prefix %}{% paste.displayDate %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!--/.well -->
|
||||
</div>
|
||||
<!--/span-->
|
||||
|
||||
<div id='main' class="span10">
|
||||
|
||||
|
||||
<p :class="'alert alert-' + msg.type" v-for="msg in messages">
|
||||
<a class="close" data-dismiss="alert" href="#" @click.prevent="$event.target.parentNode.remove()">×</a>
|
||||
<strong class="title" v-if="msg.title" v-html="msg.title"></strong>
|
||||
<span class="message" v-html="msg.content"></span>
|
||||
<a v-if="msg.action.message" href="#"
|
||||
@click.once.prevent="msg.action.callback($event)">{% msg.action.message %}</a>
|
||||
</p>
|
||||
|
||||
{{!base}}
|
||||
|
||||
</div>
|
||||
<!--/span-->
|
||||
|
||||
</div>
|
||||
<!--/row-->
|
||||
|
||||
<hr>
|
||||
|
||||
<footer>
|
||||
<blockquote>
|
||||
<p>“Few persons can be made to believe that it is not quite an easy thing to invent a method of secret writing
|
||||
which shall baffle investigation. Yet it may be roundly asserted that human ingenuity cannot concoct a cipher
|
||||
which human ingenuity cannot resolve...”</p>
|
||||
<small>Edgar Allan Poe</small>
|
||||
</blockquote>
|
||||
|
||||
|
||||
%if settings.DISPLAY_COUNTER:
|
||||
<h4 id="pixels-total">
|
||||
<p>ø</p>
|
||||
<strong>{{ pastes_count }}</strong> <br />pastes øbinned
|
||||
</h4>
|
||||
%if defined('paste') and paste.title:
|
||||
<h1 :class="{ 'reader-mode-title': readerMode}">{{ paste.title }}</h1>
|
||||
%end
|
||||
|
||||
|
||||
<br>
|
||||
<p class="greetings span12">
|
||||
Based on an original idea from
|
||||
<a href="http://sebsauvage.net/paste/">sebsauvage.net</a><br>
|
||||
<a href="http://sametmax.com">Sam & Max</a>
|
||||
<p :class="'alert alert-' + msg.type" v-for="msg in messages">
|
||||
<a class="close" data-dismiss="alert" href="#" @click.prevent="$event.target.parentNode.remove()">×</a>
|
||||
<strong class="title" v-if="msg.title" v-html="msg.title"></strong>
|
||||
<span class="message" v-html="msg.content"></span>
|
||||
<a v-if="msg.action.message" href="#"
|
||||
@click.once.prevent="msg.action.callback($event)">{% msg.action.message %}</a>
|
||||
</p>
|
||||
|
||||
<div id='main'>{{!base}}</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<ul>
|
||||
%for i, entry in enumerate(settings.MENU):
|
||||
<li>
|
||||
%if "mailto:" in entry[1]:
|
||||
<span :title='formatEmail(`{{ entry[1].replace("mailto:", "").replace("@", "__AT__") }}`)' class="email-link">
|
||||
{{ entry[0] }}
|
||||
</span>
|
||||
%else:
|
||||
<a href="{{ entry[1] }}">{{ entry[0] }}</a>
|
||||
%end
|
||||
</li>
|
||||
%end
|
||||
</ul>
|
||||
|
||||
%if settings.DISPLAY_COUNTER:
|
||||
<strong>{{ pastes_count }}</strong> pastes øbinned
|
||||
%end
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<!--/wrap-content-->
|
||||
|
||||
<script src="/static/js/vue.js"></script>
|
||||
%if settings.COMPRESSED_STATIC_FILES:
|
||||
%if not settings.DEBUG:
|
||||
<script src="/static/js/main.min.js?{{ VERSION }}"></script>
|
||||
%else:
|
||||
<script src="/static/js/vue.js"></script>
|
||||
<script src="/static/js/sjcl.js"></script>
|
||||
<script src="/static/js/behavior.js?{{ VERSION }}"></script>
|
||||
<script src="/static/js/prettify.min.js"></script>
|
||||
%end
|
||||
|
||||
<script type="text/javascript">
|
||||
zerobin.max_size = {{ settings.MAX_SIZE }};
|
||||
|
||||
</script>
|
||||
|
||||
%if settings.COMPRESSED_STATIC_FILES:
|
||||
<script src="/static/js/additional.min.js?{{ VERSION }}"></script>
|
||||
%else:
|
||||
<p id="alert-template" class="alert-primary">
|
||||
<a class="close" data-dismiss="alert" href="#">×</a>
|
||||
<strong class="title"></strong>
|
||||
<span class="message"></span>
|
||||
</p>
|
||||
|
||||
<script src="/static/js/lzw.js"></script>
|
||||
<script src="/static/js/prettify.min.js"></script>
|
||||
%end
|
||||
|
||||
</body>
|
||||
|
||||
|
85
zerobin/views/buy_bitcoin.tpl
Normal file
@ -0,0 +1,85 @@
|
||||
<div class="well" id="buy_bitcoin">
|
||||
<h1>How To Buy Bitcoin?</h1>
|
||||
|
||||
<p></p>
|
||||
|
||||
<h4>What is Bitcoin?</h4>
|
||||
|
||||
<p>Bitcoin was created by Satoshi Nakamoto, a pseudonymous person or team who outlined the technology in a 2008 white paper. It’s an appealingly simple concept: <strong>bitcoin is digital money</strong> that allows for secure peer-to-peer transactions on the internet.
|
||||
</p>
|
||||
<p>Unlike services like Venmo and PayPal, which rely on the traditional financial system for permission to transfer money and on existing debit/credit accounts, bitcoin is decentralized: any two people, anywhere in the world, can send bitcoin to each other without the involvement of a bank, government, or other institution.
|
||||
<p>
|
||||
Every transaction involving Bitcoin is tracked on the blockchain, which is similar to a bank’s ledger, or log of customers’ funds going in and out of the bank. In simple terms, it’s a record of every transaction ever made using bitcoin.
|
||||
</p>
|
||||
<p>
|
||||
There will only ever be 21 million bitcoin. This is digital money that cannot be inflated or manipulated in any way.
|
||||
</p>
|
||||
<p>
|
||||
It isn’t necessary to buy an entire bitcoin: you can buy just a fraction of one if that’s all you want or need.
|
||||
</p>
|
||||
|
||||
|
||||
<h4>Why would I buy Bitcoin?</h4>
|
||||
|
||||
<p>Bitcoin is in the news today more than ever. Thanks to skyrocketing prices and rollercoaster dips, everyone and their dogs are interested in learning how to buy and sell Bitcoin. As the most popular form of cryptocurrency, Bitcoin is now widely accepted around the world and has a growing number of applications. If you want to take advantage of that though, you first need to know how to buy Bitcoin and what to do with it when you have.
|
||||
</p>
|
||||
|
||||
<h4>How to buy Bitcoin ?</h4>
|
||||
|
||||
<p>The best place to make your first Bitcoin purchase is on an exchange. There are a whole lot of exchanges out there, with varying performance. Some are less trustworthy than others, and some can be quite limited, so it’s crucial to pick the right exchange in the first place. We recommend using <a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Binance</a>, follow the simple guide bellow to buy your first Bitcoins.
|
||||
</p>
|
||||
|
||||
|
||||
<h6>1. Buy Bitcoin on Binance</h6>
|
||||
|
||||
<p>We recommend buying Bitcoins on <a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Binance</a> because in terms of volume, it is considered to be the world's leading platform.
|
||||
In 2019, Binance reports an average trading volume of <strong>$2.8 billion per day</strong>. This platform, launched on July 14, 2017 in Hong Kong, has the advantage of operating with technical assistance 24 hours a day, 7 days a week. Its <strong>CEO Zhao Changpeng</strong>, who has become a billionaire, was featured on the cover of the prestigious Forbes magazine in February 2018.
|
||||
</p>
|
||||
|
||||
<h6>2. How do I register with <a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Binance</a> to buy Bitcoin?</h6>
|
||||
|
||||
<p>The first step before buying Bitcoin on <a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Binance</a> is to register. The user must first select the language in which they want to access the site, then enter a valid email address and finally choose a password.
|
||||
</p>
|
||||
|
||||
<h3 class="text-center"><a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Click here to register an account with Binance now!<a></h3>
|
||||
|
||||
<img src="/static/img/binance1.png" alt="Register Binance">
|
||||
|
||||
<p>For your security, the platform offers you to proceed to the 2FA, also called "Two Step Verification", which consists in logging in with your password and your phone using <a href="https://support.google.com/accounts/answer/1066447?co=GENIE.Platform%3DAndroid">Google Authenticator</a>. Both we and the platform recommend that you delay the purchase of your Bitcoins by opting for this extra layer of security. Binance Academy 2fa available <a href="https://academy.binance.com/tutorials/binance-2fa-guide">here</a>.
|
||||
</p>
|
||||
|
||||
<img src="/static/img/binance2.png" alt="2FA Binance">
|
||||
|
||||
<p>The second step is to verify your identity. Indeed, <a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Binance</a> is subject to KYC regulations as part of the fight against money laundering and terrorist financing. The platform will ask you whether or not you reside in China, before moving on to other personal information (surname, first name, gender, country of residence).
|
||||
</p>
|
||||
|
||||
<img src="/static/img/binance3.png" alt="Verify identity Binance">
|
||||
|
||||
<p>Authentication is done by sending the number and a photocopy of both sides of your ID and a photo of you. On this photo, you must show the same ID and a newspaper (or other document) with the current date.
|
||||
</p>
|
||||
|
||||
<img src="/static/img/binance4.png" alt="Verify identity Binance">
|
||||
|
||||
<p>The easiest way to get Bitcoin at Binance is to buy it by credit card. To do so, head over to the “Buy Crypto” section on the <a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Binance</a> toolbar, and choose the “Buy with Credit Card” option.
|
||||
</p>
|
||||
|
||||
<img src="/static/img/binance6.png" alt="Verify identity Binance">
|
||||
|
||||
<p>Select your currency and BTC coin you wish to purchase and enter the amount before clicking "Buy BTC". All you will need to do is enter your bank details and the purchase will be completed.</p>
|
||||
|
||||
<img src="/static/img/binance5.png" alt="Verify identity Binance">
|
||||
|
||||
<p>Although it is mainly focused on the exchange of cryptomoney, <a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Binance</a> is now establishing itself as a very competitive platform for the purchase of cryptomoney by credit card. It is also reassuring because of its reputation, its activity and its ecosystem.</p>
|
||||
|
||||
<h4>You're now a part of the future! Congrats!</h4>
|
||||
|
||||
<p></p>
|
||||
<p>Read more about Bitcoins
|
||||
<br><a href="https://bitcoin.org/en/">Bitcoin.org</a>
|
||||
</p>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
% rebase("base", settings=settings, pastes_count=pastes_count)
|
@ -1,70 +1,73 @@
|
||||
<div class="well" id="faq">
|
||||
|
||||
<h1>FAQ</h1>
|
||||
<h1>FAQ</h1>
|
||||
|
||||
<hr width="90%">
|
||||
<p></p>
|
||||
|
||||
<dl>
|
||||
<dt>How does 0bin work?</dt>
|
||||
<dd>
|
||||
<p>A random key is generated and used to encrypt the paste, thanks to
|
||||
the <a href="http://crypto.stanford.edu/sjcl/">sjcl</a>
|
||||
JavaScript library.</p>
|
||||
<p>The encrypted content is then sent to the server, which returns the
|
||||
address of the newly created paste.</p>
|
||||
<p>The JavaScript code redirects to this address, but it adds the
|
||||
encryption key in the URL hash (#).</p>
|
||||
<p>When somebody wants to read the paste, they will usually click on a link
|
||||
with this URL. If the hash containing the key is a part of it, 0bin's
|
||||
JavaScript will use it to decrypt the content sent by the server.</p>
|
||||
<p>The browser never sends the hash to the server, so the latter does not
|
||||
receives the key at any time.</p>
|
||||
</dd>
|
||||
<h4>How does 0bin work?</h4>
|
||||
|
||||
<dt>But JavaScript encryption is not secure!</dt>
|
||||
<dd>
|
||||
<p>No, it isn't.</p>
|
||||
<p>The goal of 0bin is <strong>not</strong> to protect the user and their data
|
||||
(including, obviously, their secrets).</p>
|
||||
<p>Instead, it aims to protect the host from being sued for the
|
||||
content users pasted on the pastebin. The idea is that you cannot
|
||||
require somebody to moderate something they cannot read - as such,
|
||||
the host is granted plausible deniability.</p>
|
||||
<p>A random key is generated and used to encrypt the paste, thanks to
|
||||
the <a href="http://crypto.stanford.edu/sjcl/">sjcl</a>
|
||||
JavaScript library.</p>
|
||||
<p>The encrypted content is then sent to the server, which returns the
|
||||
address of the newly created paste.</p>
|
||||
<p>The JavaScript code redirects to this address, but it adds the
|
||||
encryption key in the URL hash (#).</p>
|
||||
<p>When somebody wants to read the paste, they will usually click on a link
|
||||
with this URL. If the hash containing the key is a part of it, 0bin's
|
||||
JavaScript will use it to decrypt the content sent by the server.</p>
|
||||
<p>The browser never sends the hash to the server, so the latter does not
|
||||
receives the key at any time.</p>
|
||||
|
||||
<p>Remember that as an user, you should use 0bin in the same way as unencrypted and
|
||||
insecure pastebins - that is, with caution. The only difference with those is that if
|
||||
you decide to host a 0bin server, the encryption feature hopefully be used as a defense.
|
||||
This is not proven, though! :-)
|
||||
|
||||
</dd>
|
||||
<dt>What if the server changes the JavaScript code? And what happens in the case of a <a href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack">MITM attack</a>?</dt>
|
||||
<dd>
|
||||
<p>Read above.</p>
|
||||
<p>0bin is not built, and does not aim, to protect user data - but rather the host.
|
||||
If any user data is compromised, 0bin still provides the host with
|
||||
plausible deniability (as they ignore the content of the pastes).</p>
|
||||
<p>It would make no sense if the host was to compromise the encryption process
|
||||
to read the data; in that case, they wouldn't have
|
||||
installed 0bin in the first place, as 0bin is here to protect them.</p>
|
||||
<p><strong>However, if you want to ensure your data is not read in anyway, you should
|
||||
not use 0bin</strong>. Use <a href="http://www.cypherpunks.ca/otr/">OTR</a> for chatting,
|
||||
<a href="https://gnupg.org/">GnuPG</a> for encrypted & verified data sharing, with <a href="https://www.enigmail.net/">EnigMail</a>
|
||||
for emails.</p>
|
||||
<p>It would be unlikely for those softwares to fail you. Errors will nearly always come from your side - you ought to have a perfect <a href="https://en.wikipedia.org/wiki/Operations_security">operations security</a>
|
||||
if you do not want your data to be leaked. Remember to use your common sense.</p>
|
||||
</dd>
|
||||
<dt>How did the idea of 0bin emerge?</dt>
|
||||
<dd>
|
||||
<p>0bin is based on <a href="http://sebsauvage.net/wiki/doku.php?id=php:zerobin">sebsauvage's work</a>.
|
||||
The project sprang as a reaction to <a href="https://www.zdnet.com/blog/security/pastebin-to-hunt-for-hacker-pastes-anonymous-cries-censorship/11336">the implementation of a moderation system on Pastebin</a>,
|
||||
due to the significant amount of illegal content pasted on it, or that it linked to.</p>
|
||||
</dd>
|
||||
<dt>How can I get 0bin?</dt>
|
||||
<dd>
|
||||
<p>0bin is an open-source project, and the code is hosted on <a href="https://github.com/sametmax/0bin">GitHub</a>.
|
||||
You can either download a tarball or clone the repository.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
<h4>But JavaScript encryption is not secure!</h4>
|
||||
|
||||
<p>No, it isn't.</p>
|
||||
<p>The goal of 0bin is <strong>not</strong> to protect the user and their data
|
||||
(including, obviously, their secrets).</p>
|
||||
<p>Instead, it aims to protect the host from being sued for the
|
||||
content users pasted on the pastebin. The idea is that you cannot
|
||||
require somebody to moderate something they cannot read - as such,
|
||||
the host is granted plausible deniability.</p>
|
||||
|
||||
<p>Remember that as an user, you should use 0bin in the same way as unencrypted and
|
||||
insecure pastebins - that is, with caution. The only difference with those is that if
|
||||
you decide to host a 0bin server, the encryption feature hopefully be used as a defense.
|
||||
This is not proven, though! :-)</p>
|
||||
|
||||
|
||||
<h4>What if the server changes the JavaScript code? And what happens in the case of a <a
|
||||
href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack">MITM attack</a>?</h4>
|
||||
|
||||
<p>Read above.</p>
|
||||
<p>0bin is not built, and does not aim, to protect user data - but rather the host.
|
||||
If any user data is compromised, 0bin still provides the host with
|
||||
plausible deniability (as they ignore the content of the pastes).</p>
|
||||
<p>It would make no sense if the host was to compromise the encryption process
|
||||
to read the data; in that case, they wouldn't have
|
||||
installed 0bin in the first place, as 0bin is here to protect them.</p>
|
||||
<p><strong>However, if you want to ensure your data is not read in anyway, you should
|
||||
not use 0bin</strong>. Use <a href="http://www.cypherpunks.ca/otr/">OTR</a> for chatting,
|
||||
<a href="https://gnupg.org/">GnuPG</a> for encrypted & verified data sharing, with <a
|
||||
href="https://www.enigmail.net/">EnigMail</a>
|
||||
for emails.</p>
|
||||
<p>It would be unlikely for those softwares to fail you. Errors will nearly always come from your side - you
|
||||
ought to have a perfect <a href="https://en.wikipedia.org/wiki/Operations_security">operations security</a>
|
||||
if you do not want your data to be leaked. Remember to use your common sense.</p>
|
||||
|
||||
<h4>How did the idea of 0bin emerge?</h4>
|
||||
|
||||
<p>0bin is based on <a href="http://sebsauvage.net/wiki/doku.php?id=php:zerobin">sebsauvage's work</a>.
|
||||
The project sprang as a reaction to <a
|
||||
href="https://www.zdnet.com/blog/security/pastebin-to-hunt-for-hacker-pastes-anonymous-cries-censorship/11336">the
|
||||
implementation of a moderation system on Pastebin</a>,
|
||||
due to the significant amount of illegal content pasted on it, or that it linked to.</p>
|
||||
|
||||
<h4>How can I get 0bin?</h4>
|
||||
|
||||
<p>0bin is an open-source project, and the code is hosted on <a
|
||||
href="https://github.com/sametmax/0bin">GitHub</a>.
|
||||
You can either download a tarball or clone the repository.</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -1,38 +1,83 @@
|
||||
<p class="file-upload" v-if="support.fileUpload">
|
||||
<input type="button" class="btn btn-upload" value="Upload File" :value="isUploading ? 'Uploading...': 'Upload file'"
|
||||
:disabled="isUploading">
|
||||
<input type="file" class="hide-upload" id="file-upload" @change="handleUpload($event.target.files)">
|
||||
</p>
|
||||
|
||||
<form class="well" method="post" action="/paste/create" @submit.prevent="encryptAndSendPaste()">
|
||||
<p class="paste-option">
|
||||
<label for="expiration">Expiration:</label>
|
||||
<select id="expiration" name="expiration" v-model="newPaste.expiration">
|
||||
<option value="burn_after_reading">Burn after reading</option>
|
||||
<option selected value="1_day">1 day</option>
|
||||
<option value="1_month">1 month</option>
|
||||
<option value="never">Never</option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</p>
|
||||
<p>
|
||||
<div class="progress progress-striped active" v-show="isLoading">
|
||||
<div class="bar"></div>
|
||||
</div>
|
||||
<textarea rows="10" style="width:100%" class="input-xlarge" id="content" name="content" autofocus
|
||||
v-on:keydown.prevent.ctrl.enter="encryptAndSendPaste()"></textarea>
|
||||
</p>
|
||||
<div class="d-flex justify-content-between">
|
||||
|
||||
<div>
|
||||
<div class="file-upload" v-if="support.fileUpload">
|
||||
<label type="button" class="btn btn-primary upload-file"
|
||||
:disabled="isUploading">{% isUploading ? 'Uploading...': 'Upload file' %}
|
||||
<input type="file" class="hide-upload" id="file-upload" @change="handleUpload($event.target.files)">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group select-date paste-option">
|
||||
<div class="input-group">
|
||||
<select id="expiration" name="expiration" class="custom-select" v-model="newPaste.expiration">
|
||||
<option value="burn_after_reading">Burn after reading</option>
|
||||
<option selected value="1_day">Expire in 1 day</option>
|
||||
<option value="1_month">Expire in 1 month</option>
|
||||
<option value="never">Never expire</option>
|
||||
</select>
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="pre-wrapper">
|
||||
|
||||
<div class="progress" v-show="isLoading">
|
||||
<div class="progress-bar progress-bar-striped" role="progressbar"></div>
|
||||
</div>
|
||||
|
||||
<textarea rows="10" style="width:100%;" class="form-control" id="content" name="content" autofocus
|
||||
@keydown.ctrl.enter="encryptAndSendPaste()"></textarea>
|
||||
|
||||
<div class="paste-options">
|
||||
<h6>Optional fields (those are <em>not</em> encrypted):</h6>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Title</span>
|
||||
</div>
|
||||
<input type="text" class="form-control paste-excerpt" name="paste-excerpt"
|
||||
placeholder="Anything you type here will be visible by anyone, including search engines."
|
||||
v-model="newPaste.title" maxlength="60">
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text tip" id="basic-addon1">Tip <svg xmlns="http://www.w3.org/2000/svg" width="18"
|
||||
height="18" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm0 18v-1.511h-.5v1.511h-1v-1.511h-2.484l.25-1.489h.539c.442 0 .695-.425.695-.854v-4.444c0-.416-.242-.702-.683-.702h-.817v-1.5h2.5v-1.5h1v1.5h.5v-1.5h1v1.526c2.158.073 3.012.891 3.257 1.812.29 1.09-.429 2.005-1.046 2.228.75.192 1.789.746 1.789 2.026 0 1.742-1.344 2.908-4 2.908v1.5h-1zm-.5-5.503v2.503c1.984 0 3.344-.188 3.344-1.258 0-1.148-1.469-1.245-3.344-1.245zm0-.997c1.105 0 2.789-.078 2.789-1.25 0-1-1.039-1.25-2.789-1.25v2.5z"
|
||||
fill="#eee" /></svg></span>
|
||||
</div>
|
||||
<input type="text" class="form-control paste-btc-tip-address" name="paste-btc-tip-address"
|
||||
placeholder="Put a BTC address to ask for a tip. Leave it empty to let us use our."
|
||||
v-model="newPaste.btcTipAddress" maxlength="50">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group select-date paste-option down" v-if="displayBottomToolBar">
|
||||
<div class="input-group">
|
||||
<select id="expiration" name="expiration" class="custom-select" v-model="newPaste.expiration">
|
||||
<option value="burn_after_reading">Burn after reading</option>
|
||||
<option selected value="1_day">Expire in 1 day</option>
|
||||
<option value="1_month">Expire in 1 month</option>
|
||||
<option value="never">Never expire</option>
|
||||
</select>
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="paste-option down" v-if="displayBottomToolBar">
|
||||
<label for="expiration">Expiration:</label>
|
||||
<select id="expiration" name="expiration" v-model="newPaste.expiration">
|
||||
<option value="burn_after_reading">Burn after reading</option>
|
||||
<option selected value="1_day">1 day</option>
|
||||
<option value="1_month">1 month</option>
|
||||
<option value="never">Never</option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
|
||||
|
31
zerobin/views/login.tpl
Normal file
@ -0,0 +1,31 @@
|
||||
<form class="form-group" action="." method="post">
|
||||
<div class="login-form">
|
||||
|
||||
<div class="admin-header">
|
||||
<h3>Admin Panel</h3>
|
||||
<br>
|
||||
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-hammer" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.812 1.952a.5.5 0 0 1-.312.89c-1.671 0-2.852.596-3.616 1.185L4.857 5.073V6.21a.5.5 0 0 1-.146.354L3.425 7.853a.5.5 0 0 1-.708 0L.146 5.274a.5.5 0 0 1 0-.706l1.286-1.29a.5.5 0 0 1 .354-.146H2.84C4.505 1.228 6.216.862 7.557 1.04a5.009 5.009 0 0 1 2.077.782l.178.129z"/>
|
||||
<path fill-rule="evenodd" d="M6.012 3.5a.5.5 0 0 1 .359.165l9.146 8.646A.5.5 0 0 1 15.5 13L14 14.5a.5.5 0 0 1-.756-.056L4.598 5.297a.5.5 0 0 1 .048-.65l1-1a.5.5 0 0 1 .366-.147z"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<form>
|
||||
<label>Password:</label>
|
||||
%if status == "error":
|
||||
<div class="alert alert-danger" role="alert alert-danger">
|
||||
{{message}}
|
||||
</div>
|
||||
%end
|
||||
<div class="input-group">
|
||||
<input type="password" id="password-field" placeholder="Enter your admin password here" name="password">
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-secondary">Login</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
% rebase('base', settings=settings, pastes_count=pastes_count)
|
@ -1,6 +1,6 @@
|
||||
%if "burn_after_reading" in str(paste.expiration):
|
||||
%if keep_alive:
|
||||
<p class="alert alert-info">
|
||||
<p class="alert alert-dismissible alert-primary">
|
||||
<a class="close" data-dismiss="alert" href="#" @click.prevent="$event.target.parentNode.remove()">×</a>
|
||||
<strong class="title">Ok!</strong>
|
||||
<span class="message">
|
||||
@ -8,7 +8,7 @@
|
||||
</span>
|
||||
</p>
|
||||
%else:
|
||||
<p class="alert">
|
||||
<p class="alert alert-warning alert-dismissible">
|
||||
<a class="close" data-dismiss="alert" href="#" @click.prevent="$event.target.parentNode.remove()">×</a>
|
||||
<strong class="title">Warning!</strong>
|
||||
<span class="message">
|
||||
@ -18,55 +18,129 @@
|
||||
</p>
|
||||
%end
|
||||
%end
|
||||
|
||||
<div class="well paste-form">
|
||||
<form action="/" method="get" accept-charset="utf-8">
|
||||
<p class="lnk-option">
|
||||
<a id="clip-button" v-if="support.clipboard" href="#" @click.prevent="copyToClipboard()">Copy To Clipboard</a> |
|
||||
|
||||
<a id="email-link" href="#" @click="handleSendByEmail($event)">Email this</a>
|
||||
<div :class="{'d-flex': true, 'justify-content-between': true , 'reader-mode-tools': readerMode}">
|
||||
|
||||
<span class="paste-option btn-group top">
|
||||
<button class="btn btn-clone" @click.prevent="handleClone()"><i class="icon-camera"></i> Clone</button>
|
||||
<button class="btn" v-if="downloadLink.url">
|
||||
<a :href="downloadLink.url" :download="downloadLink.name"><i class="icon-download"></i> Download</a>
|
||||
<div class="btn-group" role="group">
|
||||
<button v-if="support.clipboard && currentPaste.type === 'text'" @click.prevent="copyToClipboard()"
|
||||
type="button" id="clip-button" class="btn btn-secondary responsive-icons">Copy to clipboard
|
||||
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-clipboard-plus" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" fill="#eee" d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/>
|
||||
<path fill-rule="evenodd" fill="#eee" d="M9.5 1h-3a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3zM8 7a.5.5 0 0 1 .5.5V9H10a.5.5 0 0 1 0 1H8.5v1.5a.5.5 0 0 1-1 0V10H6a.5.5 0 0 1 0-1h1.5V7.5A.5.5 0 0 1 8 7z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button type="button" id="email-link" class="btn btn-secondary responsive-icons" @click="handleSendByEmail($event)">
|
||||
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-envelope" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" fill="#eee" d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1H2zm13 2.383l-4.758 2.855L15 11.114v-5.73zm-.034 6.878L9.271 8.82 8 9.583 6.728 8.82l-5.694 3.44A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.739zM1 11.114l4.758-2.876L1 5.383v5.73z"/>
|
||||
</svg>
|
||||
|
||||
Email
|
||||
this</button>
|
||||
</div>
|
||||
|
||||
<button class="btn">New Paste</button>
|
||||
</span>
|
||||
</p>
|
||||
<div>
|
||||
|
||||
<div class="progress progress-striped active" v-show="isLoading">
|
||||
<div class="bar"></div>
|
||||
<span class="paste-option btn-group responsive-icons">
|
||||
<button class="btn btn-clone btn-secondary" @click.prevent="handleClone()">Clone
|
||||
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-files" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#eee" fill-rule="evenodd" d="M3 2h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2zm0 1a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1H3z"/>
|
||||
<path d="M5 0h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2v-1a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1H3a2 2 0 0 1 2-2z"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<a class="btn btn-secondary download-link" :href="currentPaste.downloadLink.url"
|
||||
:download="currentPaste.downloadLink.name">Download
|
||||
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-cloud-download-fill" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#eee" fill-rule="evenodd" d="M8 0a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 4.095 0 5.555 0 7.318 0 9.366 1.708 11 3.781 11H7.5V5.5a.5.5 0 0 1 1 0V11h4.188C14.502 11 16 9.57 16 7.773c0-1.636-1.242-2.969-2.834-3.194C12.923 1.999 10.69 0 8 0zm-.354 15.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 14.293V11h-1v3.293l-2.146-2.147a.5.5 0 0 0-.708.708l3 3z"/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<button class="btn btn-secondary">New Paste
|
||||
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-pencil-square" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#eee" d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456l-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
|
||||
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="progress-container">
|
||||
<div class="progress active" v-show="isLoading">
|
||||
<div class="progress-bar progress-bar-striped" role="progressbar"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
%expiration = paste.humanized_expiration
|
||||
%if expiration:
|
||||
<p id="expiration-tag">Expire {{ expiration }}</p>
|
||||
<span id="expiration-tag">Expire {{ expiration }}</span>
|
||||
%end
|
||||
|
||||
<p>
|
||||
<pre id="paste-content" class="prettyprint">
|
||||
<code>
|
||||
{{ paste.content }}
|
||||
</code>
|
||||
</pre>
|
||||
</p>
|
||||
<pre id="paste-content" class="prettyprint" v-if="!readerMode">
|
||||
<code>
|
||||
{{ paste.content }}
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
<p v-if="currentPaste.ownerKey">
|
||||
<button type="button" class="btn btn-danger" @click="handleDeletePaste()">Delete this paste</button>
|
||||
</p>
|
||||
<div>
|
||||
<div id="readable-paste-content" v-if="readerMode">
|
||||
{% currentPaste.content %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="paste-option btn-group bottom">
|
||||
<button class="btn btn-clone" @click.prevent="handleClone()"><i class="icon-camera"></i> Clone</button>
|
||||
<div class="paste-options-res">
|
||||
<div class="btn-group">
|
||||
<span class="input-group-text">Tip it<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18"
|
||||
viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm0 18v-1.511h-.5v1.511h-1v-1.511h-2.484l.25-1.489h.539c.442 0 .695-.425.695-.854v-4.444c0-.416-.242-.702-.683-.702h-.817v-1.5h2.5v-1.5h1v1.5h.5v-1.5h1v1.526c2.158.073 3.012.891 3.257 1.812.29 1.09-.429 2.005-1.046 2.228.75.192 1.789.746 1.789 2.026 0 1.742-1.344 2.908-4 2.908v1.5h-1zm-.5-5.503v2.503c1.984 0 3.344-.188 3.344-1.258 0-1.148-1.469-1.245-3.344-1.245zm0-.997c1.105 0 2.789-.078 2.789-1.25 0-1-1.039-1.25-2.789-1.25v2.5z"
|
||||
fill="#eee" /></svg></span>
|
||||
<a class="btn btn-primary btc-tip-address"
|
||||
href="bitcoin:{{ paste.btc_tip_address or 'bc1q4x6nwp56s9enmwtsa8um0gywpxdzeyrdluga04' }}">
|
||||
{{ paste.btc_tip_address or 'bc1q4x6nwp56s9enmwtsa8um0gywpxdzeyrdluga04'}}
|
||||
</a>
|
||||
<button v-if="support.clipboard" class="btn btn-secondary" @click.prevent="copyBTCAdressToClipboard()">
|
||||
{% this.btcCopied ? "Copied :)" : "Copy" %}
|
||||
</button>
|
||||
<a href="https://www.binance.com/en/register?ref=CAWS9NNE" class="btn btn-secondary buy-btc" target="_blank" title="Be cool, Buy Bitcoins!">Buy Bitcoins!</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn" v-if="downloadLink.url">
|
||||
<a :href="downloadLink.url" :download="downloadLink.name"><i class="icon-download"></i> Download</a>
|
||||
</button>
|
||||
|
||||
<button class="btn">New Paste</button>
|
||||
</p>
|
||||
<div class="d-flex justify-content-between down">
|
||||
<div>
|
||||
<div v-if="currentPaste.ownerKey">
|
||||
<button class="btn btn-secondary" @click.prevent="handleDeletePaste()">Delete Paste</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="paste-option btn-group responsive-icons">
|
||||
<button class="btn btn-clone btn-secondary" @click.prevent="handleClone()">Clone
|
||||
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-files" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#eee" fill-rule="evenodd" d="M3 2h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2zm0 1a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1H3z"/>
|
||||
<path d="M5 0h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2v-1a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1H3a2 2 0 0 1 2-2z"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<a class="btn btn-secondary download-link" :href="currentPaste.downloadLink.url"
|
||||
:download="currentPaste.downloadLink.name">Download
|
||||
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-cloud-download-fill" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#eee" fill-rule="evenodd" d="M8 0a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 4.095 0 5.555 0 7.318 0 9.366 1.708 11 3.781 11H7.5V5.5a.5.5 0 0 1 1 0V11h4.188C14.502 11 16 9.57 16 7.773c0-1.636-1.242-2.969-2.834-3.194C12.923 1.999 10.69 0 8 0zm-.354 15.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 14.293V11h-1v3.293l-2.146-2.147a.5.5 0 0 0-.708.708l3 3z"/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<button class="btn btn-secondary">New Paste
|
||||
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-pencil-square" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#eee" d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456l-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
|
||||
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
@ -74,39 +148,93 @@
|
||||
<!-- For cloning -->
|
||||
<div class="submit-form clone">
|
||||
<form class="well" method="post" action="/paste/create" @submit.prevent="encryptAndSendPaste()">
|
||||
<p class="paste-option">
|
||||
<label for="expiration">Expiration:</label>
|
||||
<select id="expiration" name="expiration" v-model="newPaste.expiration">
|
||||
<option value="burn_after_reading">Burn after reading</option>
|
||||
<option selected value="1_day">1 day</option>
|
||||
<option value="1_month">1 month</option>
|
||||
<option value="never">Never</option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
<button class="btn btn-danger" @click.prevent="handleCancelClone()">Cancel clone</button>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<div class="progress progress-striped active" v-show="isLoading">
|
||||
<div class="bar"></div>
|
||||
<div class="d-flex justify-content-between">
|
||||
|
||||
<div>
|
||||
<div class="file-upload">
|
||||
<button type="button" class="btn btn-info" @click.prevent="handleCancelClone()">Cancel clone</button>
|
||||
</div>
|
||||
</div>
|
||||
<textarea rows="10" style="width:100%;" class="input-xlarge" id="content" name="content" autofocus
|
||||
v-on:keydown.prevent.ctrl.enter="encryptAndSendPaste()"></textarea>
|
||||
|
||||
<div class="form-group select-date-clone paste-option">
|
||||
<div class="input-group">
|
||||
<select id="expiration" name="expiration" class="custom-select" v-model="newPaste.expiration">
|
||||
<option value="burn_after_reading">Burn after reading</option>
|
||||
<option selected value="1_day">Expire in 1 day</option>
|
||||
<option value="1_month">Expire in 1 month</option>
|
||||
<option value="never">Never expire</option>
|
||||
</select>
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<p class="paste-option down" v-if="displayBottomToolBar">
|
||||
<label for="expiration">Expiration:</label>
|
||||
<select id="expiration" name="expiration" v-model="newPaste.expiration">
|
||||
<option value="burn_after_reading">Burn after reading</option>
|
||||
<option selected value="1_day">1 day</option>
|
||||
<option value="1_month">1 month</option>
|
||||
<option value="never">Never</option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</p>
|
||||
<div class="progress-container progress-clone">
|
||||
<div class="progress active" v-show="isLoading">
|
||||
<div class="progress-bar progress-bar-striped" role="progressbar"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<textarea rows="10" style="width:100%;" class=" form-control" @keydown.ctrl.enter="encryptAndSendPaste()"
|
||||
id="content" name="content"></textarea>
|
||||
<div class="paste-options">
|
||||
<h6>Optional fields (those are <em>not</em> encrypted):</h6>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Title</span>
|
||||
</div>
|
||||
<input type="text" class="form-control paste-excerpt" name="paste-excerpt"
|
||||
placeholder="Anything you type here will be visible by anyone, including search engines."
|
||||
v-model="newPaste.title" maxlength="60">
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text" id="basic-addon1"><strong><svg xmlns="http://www.w3.org/2000/svg" width="18"
|
||||
height="18" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm0 18v-1.511h-.5v1.511h-1v-1.511h-2.484l.25-1.489h.539c.442 0 .695-.425.695-.854v-4.444c0-.416-.242-.702-.683-.702h-.817v-1.5h2.5v-1.5h1v1.5h.5v-1.5h1v1.526c2.158.073 3.012.891 3.257 1.812.29 1.09-.429 2.005-1.046 2.228.75.192 1.789.746 1.789 2.026 0 1.742-1.344 2.908-4 2.908v1.5h-1zm-.5-5.503v2.503c1.984 0 3.344-.188 3.344-1.258 0-1.148-1.469-1.245-3.344-1.245zm0-.997c1.105 0 2.789-.078 2.789-1.25 0-1-1.039-1.25-2.789-1.25v2.5z"
|
||||
fill="#eee" /></svg> tip</strong></span>
|
||||
</div>
|
||||
<input type="text" class="form-control paste-btc-tip-address" name="paste-btc-tip-address"
|
||||
placeholder="Put a BTC address to ask for a tip. Leave it empty to let us use our."
|
||||
v-model="newPaste.btcTipAddress" maxlength="50">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between" v-if="displayBottomToolBar">>
|
||||
|
||||
<div>
|
||||
<div class="file-upload">
|
||||
<button type="button" class="btn btn-info" @click.prevent="handleCancelClone()">Cancel clone</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group select-date-clone paste-option">
|
||||
<div class="input-group">
|
||||
<select id="expiration" name="expiration" class="custom-select" v-model="newPaste.expiration">
|
||||
<option value="burn_after_reading">Burn after reading</option>
|
||||
<option selected value="1_day">Expire in 1 day</option>
|
||||
<option value="1_month">Expire in 1 month</option>
|
||||
<option value="never">Never expire</option>
|
||||
</select>
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-primary">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
% rebase("base", settings=settings, pastes_count=pastes_count)
|
||||
|
10
zerobin/wsgi.py
Normal file
@ -0,0 +1,10 @@
|
||||
from zerobin.routes import get_app
|
||||
|
||||
# Remember you can set the following environment variables to configure
|
||||
# how get_app() setup the 0bin:
|
||||
#
|
||||
# - ZEROBIN_DEBUG =
|
||||
# - ZEROBIN_DATA_DIR
|
||||
# - ZEROBIN_CONFIG_DIR
|
||||
|
||||
settings, app = get_app()
|