Compare commits
87 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 |
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.
|
||||
|
63
compress.sh
@ -1,51 +1,56 @@
|
||||
#! /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; }
|
||||
command -v "yui-compressor" >/dev/null 2>&1 || {
|
||||
echo >&2 "Error: this script requires the command 'yui-compressor' to be available"
|
||||
exit 1
|
||||
}
|
||||
|
||||
CURDIR=$(dirname $(readlink -f $0));
|
||||
command -v "uglifyjs" >/dev/null 2>&1 || {
|
||||
echo >&2 "Error: this script requires the command 'uglifyjs' to be available"
|
||||
exit 1
|
||||
}
|
||||
|
||||
CURDIR=$(dirname $(readlink -f $0))
|
||||
STATICDIR=$CURDIR'/zerobin/static/'
|
||||
CSSDIR=$STATICDIR'css/'
|
||||
JSDIR=$STATICDIR'js/'
|
||||
|
||||
MAIN_JS_OUTPUT=$JSDIR"main.min.js";
|
||||
ADDITIONAL_JS_OUTPUT=$JSDIR"additional.min.js";
|
||||
MAIN_JS_OUTPUT=$JSDIR"main.min.js"
|
||||
ADDITIONAL_JS_OUTPUT=$JSDIR"additional.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
|
||||
cat $CSSDIR'prettify.css' >>$CSS_OUTPUT
|
||||
|
||||
echo $'\n''/* Desert prettify theme */' >>$CSS_OUTPUT
|
||||
cat $CSSDIR'desert.css' >>$CSS_OUTPUT
|
||||
|
||||
echo $'\n''/* Bootswatch bootstrap theme */' >>$CSS_OUTPUT
|
||||
yui-compressor $CSSDIR'bootswatch.4.5.css' >>$CSS_OUTPUT
|
||||
|
||||
echo $'\n''/* Our own CSS */' >>$CSS_OUTPUT
|
||||
yui-compressor $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"
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
CherryPy==8.9.0
|
||||
clize==4.1.1
|
||||
lockfile==0.12.2
|
||||
sigtools==2.0.2
|
||||
bottle==0.12.18
|
||||
Beaker==1.11.0
|
||||
Paste==3.4.3
|
||||
appdirs==1.4.4
|
||||
bleach==3.1.5
|
||||
|
28
setup.cfg
Normal file
@ -0,0 +1,28 @@
|
||||
[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
|
||||
sigtools==2.0.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,6 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
from zerobin.cmd import main
|
||||
|
||||
main()
|
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,106 @@ def index():
|
||||
return GLOBAL_CONTEXT
|
||||
|
||||
|
||||
@app.route("/faq/")
|
||||
@app.get("/faq/")
|
||||
@view("faq")
|
||||
def faq():
|
||||
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 +186,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 +211,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 }
|
||||
}
|
@ -12,14 +12,9 @@
|
||||
|
||||
.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 +46,9 @@
|
||||
|
||||
/* body & other stuff */
|
||||
|
||||
body {
|
||||
padding-top: 60px;
|
||||
padding-bottom: 40px;
|
||||
.blk-space {
|
||||
height: 20px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sidebar-nav {
|
||||
@ -64,22 +59,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 +91,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 +171,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 +287,8 @@ input.hide-upload {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.paste-option {
|
||||
float: right;
|
||||
.tip svg{
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
a#clip-button.hover {
|
||||
@ -222,10 +310,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 +327,71 @@ 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;
|
||||
|
||||
.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 +399,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 +406,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 +460,253 @@ 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
54
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/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>
|
||||
|
||||
|
@ -1,70 +1,74 @@
|
||||
<div class="well" id="faq">
|
||||
|
||||
<h1>FAQ</h1>
|
||||
<h1>FAQ</h1>
|
||||
|
||||
<hr width="90%">
|
||||
<table>
|
||||
<tr>How does 0bin work?</tr>
|
||||
<td>
|
||||
<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>
|
||||
</td>
|
||||
|
||||
<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>
|
||||
<tr>But JavaScript encryption is not secure!</tr>
|
||||
<td>
|
||||
<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>
|
||||
|
||||
<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>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>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>
|
||||
</td>
|
||||
<tr>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>?</tr>
|
||||
<td>
|
||||
<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>
|
||||
</td>
|
||||
<tr>How did the idea of 0bin emerge?</tr>
|
||||
<td>
|
||||
<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>
|
||||
</td>
|
||||
<tr>How can I get 0bin?</tr>
|
||||
<td>
|
||||
<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>
|
||||
</td>
|
||||
</table>
|
||||
|
||||
</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,127 @@
|
||||
</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 with<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 v-if="currentPaste.ownerKey">
|
||||
<button class="btn btn-secondary" @click.prevent="handleDeletePaste()">Delete Paste</button>
|
||||
</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 +146,94 @@
|
||||
<!-- 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>
|
||||
<label class="col-form-label"> </label>
|
||||
<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)
|
||||
|
3
zerobin/wsgi.py
Normal file
@ -0,0 +1,3 @@
|
||||
from zerobin.routes import get_app
|
||||
|
||||
settings, app = get_app()
|