[MOV] doc: move documentation to a dedicated repository

In odoo/documentation-user#945, the developer documentation is merged
with the user documentation in a single repository and build config.

This commit then removes source files for the developer documentation
from the odoo/odoo repository.

DOC PR: https://github.com/odoo/documentation-user/pull/945

task-2351938
task-2352371
task-2205684
task-2352544

Co-authored-by: Victor Feyens <vfe@odoo.com>
Co-authored-by: Antoine Vandevenne <anv@odoo.com>
This commit is contained in:
Victor Feyens
2021-05-04 08:25:12 +00:00
committed by Antoine Vandevenne (anv)
co-authored by Antoine Vandevenne
parent 40204d6575
commit 609082da2f
405 changed files with 0 additions and 69072 deletions
-166
View File
@@ -1,166 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS = -q -t solutions
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
LESSC = lessc
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
STATICS = _extensions/odoo_ext/static
# TODO: add bootstrap files
LESSFILES = $(wildcard $(STATICS)/*.less)
$(STATICS)/style.css: $(LESSFILES)
$(LESSC) $(STATICS)/style.less $(STATICS)/style.css
html: _extensions/odoo_ext/static/style.css
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
sed -i '/-99999/d' _build/dirhtml/_static/flasky.css
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/OpenERPTechnicalDocumentation.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/OpenERPTechnicalDocumentation.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/OpenERPTechnicalDocumentation"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/OpenERPTechnicalDocumentation"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
-87
View File
@@ -1,87 +0,0 @@
:orphan:
======================================
JSDoc parser/Sphinx extension for Odoo
======================================
Why?
====
Spent about a week trying to coerce "standard" javascript tools (jsdoc_ with
the hope of using sphinx-js_ for integration or `documentation.js`_) and
failed to ever get a sensible result: failed to get any result with the
current state of the documentation, significant changes/additions/fixes to
docstrings brought this up to "garbage output" level.
Bug reports and mailing list posts didn't show any path to improvement on the
ES5 codebase (if we ever go whole-hog on ES6 modules and classes things could
be different, in fact most of JSDoc's current effort seem focused on
ES6/ES2015 features) but both experience and looking at the mailing lists
told me that spending more time would be wasted.
Even more so as I was writing visitors/rewriters to generate documentation
from our existing structure, which broadly speaking is relatively strict, and
thus
What?
=====
If it were possible to generate JSDoc annotations from our relatively
well-defined code structures, it was obviously possible to extract documentary
information directly from it, hence this Odoo-specific package/extension
trying to do exactly that.
This package should eventually provide:
* a command-line interface which can be invoked via ``-m autojsdoc`` (assuming
your ``PYTHONPATH`` can find it) which should allow dumping the parsed AST
in a convenient-ish form, possibly doing searches through the AST, a
dependency graph extractor/analysis and a text dumper for the
documentation.
* a sphinx extension (``autojsdoc.sphinx``) which can be used to integrate the
parsed JSDoc information into the Sphinx doc.
How?
====
Sphinx-aside, the package relies on 3 libraries:
* pyjsparser_, an Esprima-compliant ES5.1 parser (with bits of ES6 support),
sadly it does not support comments in its current form so I had to fork it.
Fed a javascript source file, pyjsparser_ simply generates a bunch of nested
dicts representing an Esprima ast, ast-types_ does a reasonably good job of
describing it once you understand that "bases" are basically just structural
mixins.
Because the original does not, this package provides a ``visitor`` module
for pyjsparser_ ASTs.
* pyjsdoc_, a one-file "port" of jsdoc, can actually do much of the JS parsing
(using string munging) but its core semantics don't fit our needs so I'm
only using it to parse the actual JSDoc content, and the ``jsdoc`` module
contains some replacement classes, extensions & monkey patches for things
`pyjsdoc`_ itself does not support, at the time of this writing:
- a bug in FunctionDoc.return_val
- a type on FunctionDoc so it's compatible with ParamDoc
- a more reliable comments-parsing function
- a replacement ModuleDoc as the original does not materialise AMD modules
- a ClassDoc extension to support mixins
- two additional CommentDoc extensions for "namespaces" objects (bag of
attributes without any more information) and mixin objects
* pytest_ to configure and run the test suite, which you can run by invoking
``pytest doc/_extensions`` from the project top-level, the tests represent
both "happy path" things we want to parse and various code patterns which
tripped the happy path because e.g. they were matched and should not have,
they were not matched and should have, or they were more complex than the
happy path had expected
.. _ast-types: _https://github.com/benjamn/ast-types/blob/master/def/core.js
.. _documentation.js: http://documentation.js.org
.. _jsdoc: http://usejsdoc.org
.. _pyjsdoc: https://github.com/nostrademons/pyjsdoc
.. _pyjsparser: https://github.com/PiotrDabkowski/pyjsparser
.. _pytest: https://pytest.org/
.. _sphinx-js: https://sphinx-js-howto.readthedocs.io
-1
View File
@@ -1 +0,0 @@
# -*- coding: utf-8 -*-
-149
View File
@@ -1,149 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import cgitb
import fnmatch
import io
import logging
import click
import pyjsparser
import sys
from .parser.parser import ModuleMatcher
from .parser.visitor import Visitor, SKIP
from .parser import jsdoc
class Printer(Visitor):
def __init__(self, level=0):
super(Printer, self).__init__()
self.level = level
def _print(self, text):
print(' ' * self.level, text)
def enter_generic(self, node):
self._print(node['type'])
self.level += 1
def exit_generic(self, node):
self.level -= 1
def enter_Identifier(self, node):
self._print(node['name'])
return SKIP
def enter_Literal(self, node):
self._print(node['value'])
return SKIP
def enter_BinaryExpression(self, node):
self._print(node['operator'])
self.level += 1
def visit_files(files, visitor, ctx):
for name in files:
with io.open(name) as f:
ctx.logger.info("%s", name)
try:
yield visitor().visit(pyjsparser.parse(f.read()))
except Exception as e:
if ctx.logger.isEnabledFor(logging.DEBUG):
ctx.logger.exception("while visiting %s", name)
else:
ctx.logger.error("%s while visiting %s", e, name)
# bunch of modules various bits depend on which are not statically defined
# (or are outside the scope of the system)
ABSTRACT_MODULES = [
jsdoc.ModuleDoc({
'module': 'web.web_client',
'dependency': {'web.AbstractWebClient'},
'exports': jsdoc.NSDoc({
'name': 'web_client',
'doc': 'instance of AbstractWebClient',
}),
}),
jsdoc.ModuleDoc({
'module': 'web.Tour',
'dependency': {'web_tour.TourManager'},
'exports': jsdoc.NSDoc({
'name': 'Tour',
'doc': 'maybe tourmanager instance?',
}),
}),
]
@click.group(context_settings={'help_option_names': ['-h', '--help']})
@click.option('-v', '--verbose', count=True)
@click.option('-q', '--quiet', count=True)
@click.pass_context
def autojsdoc(ctx, verbose, quiet):
logging.basicConfig(
level=logging.INFO + (quiet - verbose) * 10,
format="[%(levelname)s %(created)f] %(message)s",
)
ctx.logger = logging.getLogger('autojsdoc')
ctx.visitor = None
ctx.files = []
ctx.kw = {}
@autojsdoc.command()
@click.argument('files', type=click.Path(exists=True), nargs=-1)
@click.pass_context
def ast(ctx, files):
""" Prints a structure tree of the provided files
"""
if not files:
print(ctx.get_help())
visit_files(files, lambda: Printer(level=1), ctx.parent)
@autojsdoc.command()
@click.option('-m', '--module', multiple=True, help="Only shows dependencies matching any of the patterns")
@click.argument('files', type=click.Path(exists=True), nargs=-1)
@click.pass_context
def dependencies(ctx, module, files):
""" Prints a dot file of all modules to stdout
"""
if not files:
print(ctx.get_help())
byname = {
mod.name: mod.dependencies
for mod in ABSTRACT_MODULES
}
for modules in visit_files(files, ModuleMatcher, ctx.parent):
for mod in modules:
byname[mod.name] = mod.dependencies
print('digraph dependencies {')
todo = set()
# if module filters, roots are only matching modules
if module:
for f in module:
todo.update(fnmatch.filter(byname.keys(), f))
for m in todo:
# set a different box for selected roots
print(' "%s" [color=orangered]' % m)
else:
# otherwise check all modules
todo.update(byname)
done = set()
while todo:
node = todo.pop()
if node in done:
continue
done.add(node)
deps = byname[node]
todo.update(deps - done)
for dep in deps:
print(' "%s" -> "%s";' % (node, dep))
print('}')
try:
autojsdoc.main(prog_name='autojsdoc')
except Exception:
print(cgitb.text(sys.exc_info()))
-13
View File
@@ -1,13 +0,0 @@
# -*- coding: utf-8 -*-
from .directives import automodule_bound, autodirective_bound
from .extractor import _get_roots
def setup(app):
app.add_config_value('js_roots', _get_roots, 'env')
modules = {}
app.add_directive_to_domain('js', 'automodule', automodule_bound(app, modules)
)
autodirective = autodirective_bound(app, modules)
for n in ['autonamespace', 'automixin', 'autoclass', 'autofunction']:
app.add_directive_to_domain('js', n, autodirective)
-719
View File
@@ -1,719 +0,0 @@
# -*- coding: utf-8 -*-
import abc
import collections
import contextlib
import fnmatch
import io
import re
from docutils import nodes
from docutils.parsers.rst import Directive
from docutils.statemachine import StringList
from sphinx import addnodes
from sphinx.ext.autodoc import members_set_option, bool_option, ALL
from autojsdoc.ext.extractor import read_js
from ..parser import jsdoc, types
class DocumenterError(Exception): pass
@contextlib.contextmanager
def addto(parent, newnode):
assert isinstance(newnode, nodes.Node), \
"Expected newnode to be a Node, got %s" % (type(newnode))
yield newnode
parent.append(newnode)
def documenter_for(directive, doc):
if isinstance(doc, jsdoc.FunctionDoc):
return FunctionDocumenter(directive, doc)
if isinstance(doc, jsdoc.ClassDoc):
return ClassDocumenter(directive, doc)
if isinstance(doc, jsdoc.MixinDoc):
return MixinDocumenter(directive, doc)
if isinstance(doc, (jsdoc.PropertyDoc, jsdoc.LiteralDoc)):
return PropertyDocumenter(directive, doc)
if isinstance(doc, jsdoc.InstanceDoc):
return InstanceDocumenter(directive, doc)
if isinstance(doc, jsdoc.Unknown):
return UnknownDocumenter(directive, doc)
if isinstance(doc, jsdoc.NSDoc):
return NSDocumenter(directive, doc)
raise TypeError("No documenter for %s" % type(doc))
def to_list(doc, source=None):
return StringList([
line.rstrip('\n')
for line in io.StringIO(doc)
], source=source)
DIRECTIVE_OPTIONS = {
'members': members_set_option,
'undoc-members': bool_option,
'private-members': bool_option,
'undoc-matches': bool_option,
}
def automodule_bound(app, modules):
class AutoModuleDirective(Directive):
required_arguments = 1
has_content = True
option_spec = DIRECTIVE_OPTIONS
# self.state.nested_parse(string, offset, node) => parse context for sub-content (body which can contain RST data)
# => needed for doc (converted?) and for actual directive body
def run(self):
self.env = self.state.document.settings.env
modname = self.arguments[0].strip()
# TODO: cache/memoize modules & symbols?
if not modules:
read_js(app, modules)
mods = [
(name, mod)
for name, mod in modules.items()
if fnmatch.fnmatch(name, modname)
]
ret = []
for name, mod in mods:
if mod.is_private:
continue
if name != modname and not (mod.doc or mod.exports):
# this module has no documentation, no exports and was
# not specifically requested through automodule -> skip
# unless requested
if not self.options.get('undoc-matches'):
continue
modsource = mod['sourcefile']
if modsource:
self.env.note_dependency(modsource)
# not sure what that's used for as normal xrefs are resolved using the id directly
target = nodes.target('', '', ids=['module-' + name], ismod=True)
self.state.document.note_explicit_target(target)
documenter = ModuleDocumenter(self, mod)
ret.append(target)
ret.extend(documenter.generate())
return ret
return AutoModuleDirective
def autodirective_bound(app, modules):
documenters = {
'js:autoclass': ClassDocumenter,
'js:autonamespace': NSDocumenter,
'js:autofunction': FunctionDocumenter,
'js:automixin': MixinDocumenter,
}
class AutoDirective(Directive):
required_arguments = 1
has_content = True
option_spec = DIRECTIVE_OPTIONS
def run(self):
self.env = self.state.document.settings.env
objname = self.arguments[0].strip()
if not modules:
read_js(app, modules)
# build complete path to object
path = self.env.temp_data.get('autojs:prefix', []) + objname.split('.')
# look for module/object split
for i in range(1, len(path)):
modname, objpath = '.'.join(path[:-i]), path[-i:]
module = modules.get(modname)
if module:
break
else:
raise Exception("Found no valid module in " + '.'.join(path))
item = module
# deref' namespaces until we reach the object we're looking for
for k in objpath:
item = item.get_property(k)
docclass = documenters[self.name]
return docclass(self, item).generate()
return AutoDirective
class Documenter(object):
objtype = None
def __init__(self, directive, doc):
self.directive = directive
self.env = directive.env
self.item = doc
@property
def modname(self):
return self.env.temp_data.get('autojs:module', '')
@property
def classname(self):
return self.env.temp_data.get('autojs:class', '')
def generate(self, all_members=False):
"""
:rtype: List[nodes.Node]
"""
try:
return self._generate(all_members=all_members)
except Exception as e:
raise DocumenterError("Failed to document %s" % self.item) from e
def _generate(self, all_members=False):
objname = self.item.name
prefixed = (self.item['sourcemodule'].name + '.' + objname) if self.item['sourcemodule'] else None
objtype = self.objtype
assert objtype, '%s has no objtype' % type(self)
root = addnodes.desc(domain='js', desctype=objtype, objtype=objtype)
with addto(root, addnodes.desc_signature(
module=self.modname or '',
fullname=objname,
)) as s:
s['class'] = self.classname
s['ids'] = []
if objname:
s['ids'].append(objname)
if prefixed:
s['ids'].append(prefixed)
if objtype:
s += addnodes.desc_annotation(
objtype, objtype,
nodes.Text(' '),
)
env = self.env
if objname:
env.domaindata['js']['objects'][objname] = (env.docname, objtype)
if prefixed:
env.domaindata['js']['objects'][prefixed] = (env.docname, objtype)
# TODO: linkcode_resolve
s += self.make_signature()
with addto(root, addnodes.desc_content()) as c:
# must be here otherwise nested_parse(self.content) will not have
# the prefix set
self.env.temp_data.setdefault('autojs:prefix', []).append(self.item.name)
c += self.make_content(all_members=all_members)
self.env.temp_data['autojs:prefix'].pop()
return [root]
def make_signature(self):
"""
:rtype: List[nodes.Node]
"""
return [addnodes.desc_name(self.item.name, self.item.name)]
@abc.abstractmethod
def make_content(self, all_members):
"""
:rtype: List[nodes.Node]
"""
def document_subtypes(self, subtypes):
docs = []
with with_mapping_value(self.directive.options, 'undoc-members', True):
for cls in subtypes:
docs += ClassDocumenter(self.directive, cls).generate(all_members=True)
return docs
class NSDocumenter(Documenter):
objtype = 'namespace'
def make_content(self, all_members):
doc = self.item
ret = nodes.section()
if doc.doc:
self.directive.state.nested_parse(to_list(doc.doc), 0, ret)
self.directive.state.nested_parse(self.directive.content, 0, ret)
ret += self.document_properties(all_members)
return ret.children
def should_document(self, member, name, all_members):
"""
:type member: jsdoc.CommentDoc
:type name: str
:type all_members: bool
:rtype: bool
"""
options = self.directive.options
members = options.get('members') or []
if not (all_members or members is ALL):
# if a member is requested by name, it's always documented
return name in members
# ctor params are merged into the class doc
if member.is_constructor:
return False
# only document "private" members if option is set
if self.is_private(member, name) and not options.get('private-members'):
return False
# TODO: what if member doesn't have a description but has non-desc tags set?
# TODO: add @public to force documenting symbol? => useful for implicit typedef
return bool(member.doc or options.get('undoc-members'))
def is_private(self, member, name):
return member.is_private
def document_properties(self, all_members):
ret = nodes.section()
# TODO: :member-order: [alphabetical | groupwise | bysource]
for (n, p) in self.item.properties:
if not self.should_document(p, n, all_members):
continue
# FIXME: maybe should use property name as name inside?
ret += documenter_for(self.directive, p).generate(all_members=True)
return ret.children
_NONE = object()
@contextlib.contextmanager
def with_mapping_value(mapping, key, value, restore_to=_NONE):
""" Sets ``key`` to ``value`` for the duration of the context.
If ``restore_to`` is not provided, restores ``key``'s old value
afterwards, removes it entirely if there was no value for ``key`` in the
mapping.
.. warning:: for defaultdict & similar mappings, may restore the default
value (depends how the collections' .get behaves)
"""
if restore_to is _NONE:
restore_to = mapping.get(key, _NONE)
mapping[key] = value
try:
yield
finally:
if restore_to is _NONE:
del mapping[key]
else:
mapping[key] = restore_to
class ModuleDocumenter(NSDocumenter):
objtype = 'module'
def document_properties(self, all_members):
with with_mapping_value(self.env.temp_data, 'autojs:module', self.item.name, ''):
return super(ModuleDocumenter, self).document_properties(all_members)
def make_content(self, all_members):
doc = self.item
content = addnodes.desc_content()
if doc.exports or doc.dependencies:
with addto(content, nodes.field_list()) as fields:
if doc.exports:
with addto(fields, nodes.field()) as field:
field += nodes.field_name('Exports', 'Exports')
with addto(field, nodes.field_body()) as body:
ref = doc['exports'] # warning: not the same as doc.exports
label = ref or '<anonymous>'
link = addnodes.pending_xref(
ref, nodes.paragraph(ref, label),
refdomain='js',
reftype='any',
reftarget=ref,
)
link['js:module'] = doc.name
body += link
if doc.dependencies:
with addto(fields, nodes.field()) as field:
self.make_dependencies(field, doc)
if doc.doc:
# FIXME: source offset
self.directive.state.nested_parse(to_list(doc.doc, source=doc['sourcefile']), 0, content)
self.directive.state.nested_parse(self.directive.content, 0, content)
content += self.document_properties(all_members)
return content
def make_dependencies(self, field, doc):
field += nodes.field_name("Depends On", "Depends On")
with addto(field, nodes.field_body()) as body:
with addto(body, nodes.bullet_list()) as deps:
for dep in sorted(doc.dependencies):
ref = addnodes.pending_xref(
dep, nodes.paragraph(dep, dep),
refdomain='js',
reftype='module',
reftarget=dep,
)
deps += nodes.list_item(dep, ref)
def should_document(self, member, name, all_members):
# member can be Nothing?
if not member:
return False
modname = getattr(member['sourcemodule'], 'name', None)
doc = self.item
# always document exported symbol (regardless undoc, private, ...)
# otherwise things become... somewhat odd
if name == doc['exports']:
return True
# if doc['exports'] the module is exporting a "named" item which
# does not need to be documented twice, if not doc['exports'] it's
# exporting an anonymous item (e.g. object literal) which needs to
# be documented on its own
if name == '<exports>' and not doc['exports']:
return True
# TODO: :imported-members:
# FIXME: *directly* re-exported "foreign symbols"?
return (not modname or modname == doc.name) \
and super(ModuleDocumenter, self).should_document(member, name, all_members)
class ClassDocumenter(NSDocumenter):
objtype = 'class'
def document_properties(self, all_members):
with with_mapping_value(self.env.temp_data, 'autojs:class', self.item.name, ''):
return super(ClassDocumenter, self).document_properties(all_members)
def make_signature(self):
sig = super(ClassDocumenter, self).make_signature()
sig.append(self.make_parameters())
return sig
def make_parameters(self):
params = addnodes.desc_parameterlist('', '')
ctor = self.item.constructor
if ctor:
params += make_desc_parameters(ctor.params)
return params
def make_content(self, all_members):
doc = self.item
ret = nodes.section()
ctor = self.item.constructor
params = subtypes = []
if ctor:
check_parameters(self, ctor)
params, subtypes = extract_subtypes(doc.name, ctor)
fields = nodes.field_list()
fields += self.make_super()
fields += self.make_mixins()
fields += self.make_params(params)
if fields.children:
ret += fields
if doc.doc:
self.directive.state.nested_parse(to_list(doc.doc), 0, ret)
self.directive.state.nested_parse(self.directive.content, 0, ret)
ret += self.document_properties(all_members)
ret += self.document_subtypes(subtypes)
return ret.children
def is_private(self, member, name):
return name.startswith('_') or super(ClassDocumenter, self).is_private(member, name)
def make_super(self):
doc = self.item
if not doc.superclass:
return []
sup_link = addnodes.pending_xref(
doc.superclass.name, nodes.paragraph(doc.superclass.name, doc.superclass.name),
refdomain='js', reftype='class', reftarget=doc.superclass.name,
)
sup_link['js:module'] = doc.superclass['sourcemodule'].name
return nodes.field(
'',
nodes.field_name("Extends", "Extends"),
nodes.field_body(doc.superclass.name, sup_link),
)
def make_mixins(self):
doc = self.item
if not doc.mixins:
return []
ret = nodes.field('', nodes.field_name("Mixes", "Mixes"))
with addto(ret, nodes.field_body()) as body:
with addto(body, nodes.bullet_list()) as mixins:
for mixin in sorted(doc.mixins, key=lambda m: m.name):
mixin_link = addnodes.pending_xref(
mixin.name, nodes.paragraph(mixin.name, mixin.name),
refdomain='js', reftype='mixin', reftarget=mixin.name
)
mixin_link['js:module'] = mixin['sourcemodule'].name
mixins += nodes.list_item('', mixin_link)
return ret
def make_params(self, params):
if not params:
return []
ret = nodes.field('', nodes.field_name('Parameters', 'Parameters'))
with addto(ret, nodes.field_body()) as body,\
addto(body, nodes.bullet_list()) as holder:
holder += make_parameters(params, mod=self.modname)
return ret
class InstanceDocumenter(Documenter):
objtype = 'object'
def make_signature(self):
cls = self.item.cls
ret = super(InstanceDocumenter, self).make_signature()
if cls:
super_ref = addnodes.pending_xref(
cls.name, nodes.Text(cls.name, cls.name),
refdomain='js', reftype='class', reftarget=cls.name
)
super_ref['js:module'] = cls['sourcemodule'].name
ret.append(addnodes.desc_annotation(' instance of ', ' instance of '))
ret.append(addnodes.desc_type(cls.name, '', super_ref))
if not ret:
return [addnodes.desc_name('???', '???')]
return ret
def make_content(self, all_members):
ret = nodes.section()
if self.item.doc:
self.directive.state.nested_parse(to_list(self.item.doc), 0, ret)
self.directive.state.nested_parse(self.directive.content, 0, ret)
return ret.children
class FunctionDocumenter(Documenter):
@property
def objtype(self):
return 'method' if self.classname else 'function'
def make_signature(self):
ret = super(FunctionDocumenter, self).make_signature()
with addto(ret, addnodes.desc_parameterlist()) as params:
params += make_desc_parameters(self.item.params)
retval = self.item.return_val
if retval.type or retval.doc:
ret.append(addnodes.desc_returns(retval.type or '*', retval.type or '*'))
return ret
def make_content(self, all_members):
ret = nodes.section()
doc = self.item
if doc.doc:
self.directive.state.nested_parse(to_list(doc.doc), 0, ret)
self.directive.state.nested_parse(self.directive.content, 0, ret)
check_parameters(self, doc)
params, subtypes = extract_subtypes(self.item.name, self.item)
rdoc = doc.return_val.doc
rtype = doc.return_val.type
if params or rtype or rdoc:
with addto(ret, nodes.field_list()) as fields:
if params:
with addto(fields, nodes.field()) as field:
field += nodes.field_name('Parameters', 'Parameters')
with addto(field, nodes.field_body()) as body,\
addto(body, nodes.bullet_list()) as holder:
holder.extend(make_parameters(params, mod=doc['sourcemodule'].name))
if rdoc:
with addto(fields, nodes.field()) as field:
field += nodes.field_name("Returns", "Returns")
with addto(field, nodes.field_body()) as body,\
addto(body, nodes.paragraph()) as p:
p += nodes.inline(rdoc, rdoc)
if rtype:
with addto(fields, nodes.field()) as field:
field += nodes.field_name("Return Type", "Return Type")
with addto(field, nodes.field_body()) as body, \
addto(body, nodes.paragraph()) as p:
p += make_types(rtype, mod=doc['sourcemodule'].name)
ret += self.document_subtypes(subtypes)
return ret.children
def pascal_case_ify(name):
"""
Uppercase first letter of ``name``, or any letter following an ``_``. In
the latter case, also strips out the ``_``.
=> key_for becomes KeyFor
=> options becomes Options
"""
return re.sub(r'(^|_)\w', lambda m: m.group(0)[-1].upper(), name)
def extract_subtypes(parent_name, doc):
""" Extracts composite parameters (a.b) into sub-types for the parent
parameter, swaps the parent's type from whatever it is to the extracted
one, and returns the extracted type for inclusion into the parent.
:arg parent_name: name of the containing symbol (function, class), will
be used to compose subtype names
:type parent_name: str
:type doc: FunctionDoc
:rtype: (List[ParamDoc], List[ClassDoc])
"""
# map of {param_name: [ParamDoc]} (from complete doc)
subparams = collections.defaultdict(list)
for p in map(jsdoc.ParamDoc, doc.get_as_list('param')):
pair = p.name.split('.', 1)
if len(pair) == 2:
k, p.name = pair # remove prefix from param name
subparams[k].append(p)
# keep original params order as that's the order of formal parameters in
# the function signature
params = collections.OrderedDict((p.name, p) for p in doc.params)
subtypes = []
# now we can use the subparams map to extract "compound" parameter types
# and swap the new type for the original param's type
for param_name, subs in subparams.items():
typename = '%s%s' % (
pascal_case_ify(parent_name),
pascal_case_ify(param_name),
)
param = params[param_name]
param.type = typename
subtypes.append(jsdoc.ClassDoc({
'name': typename,
'doc': param.doc,
'_members': [
# TODO: add default value
(sub.name, jsdoc.PropertyDoc(dict(sub.to_dict(), sourcemodule=doc['sourcemodule'])))
for sub in subs
],
'sourcemodule': doc['sourcemodule'],
}))
return params.values(), subtypes
def check_parameters(documenter, doc):
"""
Check that all documented parameters match a formal parameter for the
function. Documented params which don't match the actual function may be
typos.
"""
guessed = set(doc['guessed_params'] or [])
if not guessed:
return
documented = {
# param name can be of the form [foo.bar.baz=default]\ndescription
jsdoc.ParamDoc(text).name.split('.')[0]
for text in doc.get_as_list('param')
}
odd = documented - guessed
if not odd:
return
app = documenter.directive.env.app
app.warn("Found documented params %s not in formal parameter list "
"of function %s in module %s (%s)" % (
', '.join(odd),
doc.name,
documenter.modname,
doc['sourcemodule']['sourcefile'],
))
def make_desc_parameters(params):
for p in params:
if '.' in p.name:
continue
node = addnodes.desc_parameter(p.name, p.name)
if p.optional:
node = addnodes.desc_optional('', '', node)
yield node
def make_parameters(params, mod=None):
for param in params:
p = nodes.paragraph('', '', nodes.strong(param.name, param.name))
if param.default is not None:
p += nodes.Text('=', '=')
p += nodes.emphasis(param.default, param.default)
if param.type:
p += nodes.Text(' (')
p += make_types(param.type, mod=mod)
p += nodes.Text(')')
if param.doc:
p += [
nodes.Text(' -- '),
nodes.inline(param.doc, param.doc)
]
yield p
def _format_value(v):
if v == '|':
return nodes.emphasis(' or ', ' or ')
if v == ',':
return nodes.Text(', ', ', ')
return nodes.Text(v, v)
def make_types(typespec, mod=None):
# TODO: in closure notation {type=} => optional, do we care?
def format_type(t):
ref = addnodes.pending_xref(
t, addnodes.literal_emphasis(t, t),
refdomain='js', reftype='class', reftarget=t,
)
if mod:
ref['js:module'] = mod
return ref
try:
return types.iterate(
types.parse(typespec),
format_type,
_format_value
)
except ValueError as e:
raise ValueError("%s in '%s'" % (e, typespec))
class MixinDocumenter(NSDocumenter):
objtype = 'mixin'
class PropertyDocumenter(Documenter):
objtype = 'attribute'
def make_signature(self):
ret = super(PropertyDocumenter, self).make_signature()
proptype = self.item.type
if proptype:
typeref = addnodes.pending_xref(
proptype, nodes.Text(proptype, proptype),
refdomain='js', reftype='class', reftarget=proptype
)
typeref['js:module'] = self.item['sourcemodule'].name
ret.append(nodes.Text(' '))
ret.append(typeref)
return ret
def make_content(self, all_members):
doc = self.item
ret = nodes.section()
self.directive.state.nested_parse(self.directive.content, 0, ret)
if doc.doc:
self.directive.state.nested_parse(to_list(doc.doc), 0, ret)
return ret.children
class UnknownDocumenter(Documenter):
objtype = 'unknown'
def make_content(self, all_members):
return []
@@ -1,83 +0,0 @@
# -*- coding: utf-8 -*-
import io
import os
import pyjsdoc
import pyjsparser
try:
from sphinx.util import status_iterator
def it(app): return status_iterator
except ImportError:
# 1.2: Builder.status_iterator
# 1.3: Application.status_iterator (with alias on Builder)
# 1.6: sphinx.util.status_iterator (with *deprecated* aliases on Application and Builder)
# 1.7: removed Application and Builder aliases
# => if no sphinx.util.status_iterator, fallback onto the builder one for
# 1.2 compatibility, remove this entirely if we ever require 1.6+
status_iterator = None
def it(app): return app.builder.status_iterator
from ..parser import jsdoc, parser
def _get_roots(conf):
env_roots = os.environ.get('AUTOJSDOC_ROOTS_PATH')
if env_roots:
return env_roots.split(':')
return []
def read_js(app, modules):
"""
:type app: sphinx.application.Sphinx
:type modules: Dict[str, jsdoc.ModuleDoc]
"""
roots = map(os.path.normpath, app.config.js_roots or [os.path.join(app.confdir, '..')])
files = [
os.path.join(r, f)
for root in roots
for r, _, fs in os.walk(root)
if 'static/src/js' in r
for f in fs
if f.endswith('.js')
]
modules.update((mod.name, mod) for mod in ABSTRACT_MODULES)
for name in it(app)(files, "Parsing javascript files...", length=len(files)):
with io.open(name) as f:
ast = pyjsparser.parse(f.read())
modules.update(
(mod.name, mod)
for mod in parser.ModuleMatcher(name).visit(ast)
)
_resolve_references(modules)
def _resolve_references(byname):
# must be done in topological order otherwise the dependent can't
# resolve non-trivial references to a dependency properly
for name in pyjsdoc.topological_sort(
*pyjsdoc.build_dependency_graph(
list(byname.keys()),
byname
)
):
byname[name].post_process(byname)
ABSTRACT_MODULES = [
jsdoc.ModuleDoc({
'module': u'web.web_client',
'dependency': {u'web.AbstractWebClient'},
'exports': jsdoc.NSDoc({
'name': u'web_client',
'doc': u'instance of AbstractWebClient',
}),
}),
jsdoc.ModuleDoc({
'module': u'web.Tour',
'dependency': {u'web_tour.TourManager'},
'exports': jsdoc.NSDoc({
'name': u'Tour',
'doc': u'maybe tourmanager instance?',
}),
}),
]
@@ -1 +0,0 @@
# -*- coding: utf-8 -*-
-357
View File
@@ -1,357 +0,0 @@
# -*- coding: utf-8 -*-
import re
import collections
import pyjsdoc
def strip_stars(doc_comment):
"""
Version of jsdoc.strip_stars which always removes 1 space after * if
one is available.
"""
return re.sub('\n\s*?\*[\t ]?', '\n', doc_comment[3:-2]).strip()
class ParamDoc(pyjsdoc.ParamDoc):
"""
Replace ParamDoc because FunctionDoc doesn't properly handle optional
params or default values (TODO: or compounds) if guessed_params is used
=> augment paramdoc with "required" and "default" items to clean up name
"""
def __init__(self, text):
super(ParamDoc, self).__init__(text)
# param name and doc can be separated by - or :, strip it
self.doc = self.doc.strip().lstrip('-:').lstrip()
self.optional = False
self.default = None
# there may not be a space between the param name and the :, in which
# case the : gets attached to the name, strip *again*
# TODO: formal @param/@property parser to handle this crap properly once and for all
self.name = self.name.strip().rstrip(':')
if self.name.startswith('['):
self.name = self.name.strip('[]')
self.optional = True
if '=' in self.name:
self.name, self.default = self.name.rsplit('=', 1)
def to_dict(self):
d = super(ParamDoc, self).to_dict()
d['optional'] = self.optional
d['default'] = self.default
return d
pyjsdoc.ParamDoc = ParamDoc
class CommentDoc(pyjsdoc.CommentDoc):
namekey = object()
is_constructor = False
@property
def name(self):
return self[self.namekey] or self['name'] or self['guessed_name']
def set_name(self, name):
# not great...
if name != '<exports>':
self.parsed['guessed_name'] = name
@property
def is_private(self):
return 'private' in self.parsed
def to_dict(self):
d = super(CommentDoc, self).to_dict()
d['name'] = self.name
return d
# don't resolve already resolved docs (e.g. a literal dict being
# include-ed in two different classes because I don't even care anymore
def become(self, modules):
return self
class PropertyDoc(CommentDoc):
@classmethod
def from_param(cls, s, sourcemodule=None):
parsed = ParamDoc(s).to_dict()
parsed['sourcemodule'] = sourcemodule
return cls(parsed)
@property
def type(self):
return self['type'].strip('{}')
def to_dict(self):
d = super(PropertyDoc, self).to_dict()
d['type'] = self.type
d['is_private'] = self.is_private
return d
class InstanceDoc(CommentDoc):
@property
def cls(self):
return self['cls']
def to_dict(self):
return dict(super(InstanceDoc, self).to_dict(), cls=self.cls)
class LiteralDoc(CommentDoc):
@property
def type(self):
if self['type']:
return self['type']
valtype = type(self['value'])
if valtype is bool:
return 'Boolean'
elif valtype is float:
return 'Number'
elif valtype is type(u''):
return 'String'
return ''
@property
def value(self):
return self['value']
def to_dict(self):
d = super(LiteralDoc, self).to_dict()
d['type'] = self.type
d['value'] = self.value
return d
class FunctionDoc(CommentDoc):
type = 'Function'
namekey = 'function'
@property
def is_constructor(self):
return self.name == 'init'
@property
def params(self):
tag_texts = self.get_as_list('param')
# turns out guessed_params is *almost* (?) always set to a list,
# if empty list of guessed params fall back to @params
if not self['guessed_params']:
# only get "primary" params (no "." in name)
return [
p for p in map(ParamDoc, tag_texts)
if '.' not in p.name
]
else:
param_dict = {}
for text in tag_texts:
param = ParamDoc(text)
param_dict[param.name] = param
return [param_dict.get(name) or ParamDoc('{} ' + name)
for name in self.get('guessed_params')]
@property
def return_val(self):
ret = self.get('return') or self.get('returns')
type = self.get('type')
if '{' in ret and '}' in ret:
if not '} ' in ret:
# Ensure that name is empty
ret = re.sub(r'\}\s*', '} ', ret)
return ParamDoc(ret)
if ret and type:
return ParamDoc('{%s} %s' % (type, ret))
return ParamDoc(ret)
def to_dict(self):
d = super(FunctionDoc, self).to_dict()
d['name'] = self.name
d['params'] = [param.to_dict() for param in self.params]
d['return_val']= self.return_val.to_dict()
return d
class NSDoc(CommentDoc):
namekey = 'namespace'
def __init__(self, parsed_comment):
super(NSDoc, self).__init__(parsed_comment)
self.members = collections.OrderedDict()
def add_member(self, name, member):
"""
:type name: str
:type member: CommentDoc
"""
member.set_name(name)
self.members[name] = member
@property
def properties(self):
if self.get('property'):
return [
(p.name, p)
for p in (
PropertyDoc.from_param(p, self['sourcemodule'])
for p in self.get_as_list('property')
)
]
return list(self.members.items()) or self['_members'] or []
def has_property(self, name):
return self.get_property(name) is not None
def get_property(self, name):
return next((p for n, p in self.properties if n == name), None)
def to_dict(self):
d = super(NSDoc, self).to_dict()
d['properties'] = [(n, p.to_dict()) for n, p in self.properties]
return d
class MixinDoc(NSDoc):
namekey = 'mixin'
class ModuleDoc(NSDoc):
namekey = 'module'
def __init__(self, parsed_comment):
super(ModuleDoc, self).__init__(parsed_comment)
#: callbacks to run with the modules mapping once every module is resolved
self._post_process = []
def post_process(self, modules):
for callback in self._post_process:
callback(modules)
@property
def module(self):
return self # lol
@property
def dependencies(self):
"""
Returns the immediate dependencies of a module (only those explicitly
declared/used).
"""
return self.get('dependency', None) or set()
@property
def exports(self):
"""
Returns the actual item exported from the AMD module, can be a
namespace, a class, a function, an instance, ...
"""
return self.get_property('<exports>')
def to_dict(self):
vars = super(ModuleDoc, self).to_dict()
vars['dependencies'] = self.dependencies
vars['exports'] = self.exports
return vars
def __str__(self):
s = super().__str__()
if self['sourcefile']:
s += " in file " + self['sourcefile']
return s
class ClassDoc(NSDoc):
namekey = 'class'
@property
def constructor(self):
return self.get_property('init')
@property
def superclass(self):
return self['extends'] or self['base']
def get_property(self, method_name):
if method_name == 'extend':
return FunctionDoc({
'doc': 'Create subclass for %s' % self.name,
'guessed_function': 'extend',
})
# FIXME: should ideally be a proxy namespace
if method_name == 'prototype':
return self
return super(ClassDoc, self).get_property(method_name)\
or (self.superclass and self.superclass.get_property(method_name))
@property
def mixins(self):
return self.get_as_list('mixes')
def to_dict(self):
d = super(ClassDoc, self).to_dict()
d['mixins'] = self.mixins
return d
DEFAULT = object()
class UnknownNS(NSDoc):
params = () # TODO: log warning when (somehow) trying to access / document an unknown object as ctor?
def get_property(self, name):
return super(UnknownNS, self).get_property(name) or \
UnknownNS({'name': '{}.{}'.format(self.name, name)})
def __getitem__(self, item):
if self._probably_not_property(item):
return super().__getitem__(item)
return self.get_property(item)
def _probably_not_property(self, item):
return (
not isinstance(item, str)
or item in (self.namekey, 'name', 'params')
or item.startswith(('_', 'guessed_'))
or item in self.parsed
)
class Unknown(CommentDoc):
@classmethod
def from_(cls, source):
def builder(parsed):
inst = cls(parsed)
inst.parsed['source'] = source
return inst
return builder
@property
def name(self):
return self['name'] + ' ' + self['source']
@property
def type(self):
return "Unknown"
def get_property(self, p):
return Unknown(dict(self.parsed, source=self.name, name=p + '<'))
def parse_comments(comments, doctype=None):
# find last comment which starts with a *
docstring = next((
c['value']
for c in reversed(comments or [])
if c['value'].startswith(u'*')
), None) or u""
# \n prefix necessary otherwise parse_comment fails to take first
# block comment parser strips delimiters, but strip_stars fails without
# them
extract = '\n' + strip_stars('/*' + docstring + '\n*/')
parsed = pyjsdoc.parse_comment(extract, u'')
if doctype == 'FunctionExpression':
doctype = FunctionDoc
elif doctype == 'ObjectExpression' or doctype is None:
doctype = guess
if doctype is guess:
return doctype(parsed)
# in case a specific doctype is given, allow overriding it anyway
return guess(parsed, default=doctype)
def guess(parsed, default=UnknownNS):
if 'class' in parsed:
return ClassDoc(parsed)
if 'function' in parsed:
return FunctionDoc(parsed)
if 'mixin' in parsed:
return MixinDoc(parsed)
if 'namespace' in parsed:
return NSDoc(parsed)
if 'module' in parsed:
return ModuleDoc(parsed)
if 'type' in parsed:
return PropertyDoc(parsed)
return default(parsed)
-572
View File
@@ -1,572 +0,0 @@
# -*- coding: utf-8 -*-
import collections
import pyjsdoc
from . import jsdoc
from . import utils
from .visitor import Visitor, SKIP
DECLARATOR_INIT_TO_REF = ('Literal', 'Identifier', 'MemberExpression')
class ModuleMatcher(Visitor):
"""Looks for structures of the form::
odoo.define($string, function ($name) {
These are *Odoo module definitions*, upon encountering one the
matcher:
* creates a module entry, optionally associated with the module comment
* spawns off a :class:`ModuleBodyMatcher` on the function's body with the
module name and the $name as "require" function
"""
def __init__(self, filename):
super(ModuleMatcher, self).__init__()
self.filename = filename
self.result = []
def enter_Program(self, node):
pass # allows visiting toplevel
def enter_ExpressionStatement(self, node):
# we're interested in expression statements (toplevel call)
if utils.match(node, {'expression': {
'callee': {
'object': {'name': 'odoo'},
'property': {'name': 'define'},
},
}}):
[module, *_, func] = node['expression']['arguments']
mod = jsdoc.parse_comments(node.get('comments'), jsdoc.ModuleDoc)
# set module name
mod.set_name(module['value'])
mod.parsed['sourcefile'] = self.filename
self.result.append(mod)
# get name of require parameter
require = None # a module can have no dependencies
if func['params']:
require = func['params'][0]['name']
mod.parsed['dependency'], post = ModuleExtractor(mod, require).visit(func['body'])
mod._post_process.extend(post)
# don't recurse since we've fired off a sub-visitor for the
# bits we're interested in
return SKIP
def enter_generic(self, node):
# skip all other toplevel statements
return SKIP
ref = collections.namedtuple('Ref', 'object property')
def _name(r):
bits = []
while isinstance(r, ref):
bits.append(str(r.property))
r = r.object
return '.'.join(reversed(bits))
def deref(item, prop=None):
assert isinstance(item, ref)
while isinstance(item, ref):
obj = item.object
if isinstance(obj, ref):
obj = deref(obj)
if isinstance(obj, (jsdoc.NSDoc, jsdoc.Unknown)):
item = obj.get_property(item.property)
elif isinstance(obj, dict):
item = obj[item.property]
elif isinstance(obj, jsdoc.PropertyDoc):
# f'n dynamic crap
item = jsdoc.Unknown(obj.to_dict()).get_property(item.property)
else:
raise ValueError("%r (%s) should be a dict or namespace" % (obj, type(obj)))
return item
def m2r(me, scope):
# create a ref to the scope in case it's a hoisted function declaration,
# this may false-positive on other hoisting but w/e
if me['type'] == 'Literal':
return jsdoc.LiteralDoc({'value': me['value']})
if me['type'] == 'Identifier':
return ref(scope, me['name'])
if me['type'] != 'MemberExpression':
raise ValueError(me)
return ref(
m2r(me['object'], scope),
utils._value(me['property'], strict=True)
)
NOTHING = object()
class Declaration(object):
__slots__ = ['id', 'comments']
def __init__(self, id=None, comments=NOTHING):
self.id = id
self.comments = [] if comments is NOTHING else comments
class ModuleContent(object):
__slots__ = ['dependencies', 'post']
def __init__(self, dependencies=NOTHING, post=NOTHING):
self.dependencies = set() if dependencies is NOTHING else dependencies
self.post = [] if post is NOTHING else post
def __iter__(self):
yield self.dependencies
yield self.post
class Nothing(object):
def __init__(self, name):
self.name = name
def __bool__(self):
return False
__nonzero__ = __bool__
class RefProxy(object):
def __init__(self, r):
self._ref = r
def become(self, modules):
s = self
other = deref(self._ref) or Nothing(_name(self._ref))
# ??? shouldn't all previous refs have been resolved?
if isinstance(other, (RefProxy, ModuleProxy)):
other = other.become(modules)
self.__class__ = other.__class__
self.__dict__ = other.__dict__
return self
def set_name(self, name):
pass # ???
class ModuleProxy(object):
def __init__(self, name):
self._name = name
# replace the ModuleProxy by the module's exports
def become(self, modules):
s = self
m = modules[self._name].get_property('<exports>') or Nothing(self._name)
self.__class__ = m.__class__
self.__dict__ = m.__dict__
return self
def set_name(self, name):
pass # FIXME: ???
jq = jsdoc.UnknownNS({
'name': u'jQuery',
'doc': u'<jQuery>',
})
window = jsdoc.UnknownNS({
'doc': '<window>',
'name': 'window',
})
class BaseScope(collections.defaultdict):
""" The base scope assumes anything it's asked for is just an unknown
(global) namespace of some sort. Can hold a bunch of predefined params but
avoids the variables inference system blowing up when new (browser)
globals get used in module bodies.
"""
def __missing__(self, key):
it = jsdoc.UnknownNS({
'name': key,
'doc': u'<%s>' % key,
})
self[key] = it
return it
BASE_SCOPE = BaseScope(None, {
'_': jsdoc.UnknownNS({'doc': u'<underscore.js>', 'name': u'_'}),
'$': jq, 'jQuery': jq,
'window': window,
'document': window.get_property('document'),
'Date': jsdoc.ClassDoc({
'name': u'Date',
'doc': u'',
}),
'Backbone': jsdoc.UnknownNS({
'_members': [
('Model', jsdoc.ClassDoc({
'name': u'Model',
'doc': u'',
})),
('Collection', jsdoc.ClassDoc({
'name': u'Collection',
'doc': u'',
})),
]
}),
'odoo': jsdoc.UnknownNS({
'name': u'odoo',
'doc': u"Odoo",
'_members': [
('name', jsdoc.PropertyDoc({'name': u'csrf_token', 'type': u'{String}'})),
]
}),
'undefined': jsdoc.LiteralDoc({'name': u'undefined', 'value': None}),
})
class Scope(object):
"""
Add hoc scope versioning/SSA such that rebinding a symbol in a module
scope does not screw everything up e.g. "Foo = Foo.extend({})" should not
have the final Foo extending itself...
"""
def __init__(self, mapping):
self._namemap = self._empty(mapping)
self._targets = []
for k, v in mapping.items():
self[k] = v
@staticmethod
def _empty(mapping):
m = mapping.copy()
m.clear()
return m
def __setitem__(self, k, v):
self._namemap[k] = len(self._targets)
self._targets.append(v)
def freeze(self):
d = self._empty(self._namemap)
for k, v in self._namemap.items():
d[k] = self._targets[v]
return d
class ModuleExtractor(Visitor):
def __init__(self, module, requirefunc):
super(ModuleExtractor, self).__init__()
self.module = module
self.requirefunc = requirefunc
self.result = ModuleContent()
self.scope = Scope(BASE_SCOPE)
self.declaration = None
def enter_BlockStatement(self, node):
Hoistifier(self).visit(node)
def exit_BlockStatement(self, node):
for k, v in self.scope._namemap.items():
if k not in BASE_SCOPE:
self.module.add_member(k, self.scope._targets[v])
for t in TypedefMatcher(self).visit(node):
self.module.add_member(t.name, t)
def enter_VariableDeclaration(self, node):
self.declaration = Declaration(comments=node.get('comments'))
def enter_VariableDeclarator(self, node):
# otherwise we've already hoisted the declaration so the variable
# already exist initialised to undefined, and ValueExtractor does not
# handle a None input node so it returns None which makes no sense
if node['init']:
self.declaration.id = node['id']['name']
self.scope[self.declaration.id] = ValueExtractor(
self, self.declaration
).visit(node['init'] or [])
self.declaration.id = None
return SKIP
def exit_VariableDeclaration(self, node):
self.declaration = None
# as the name denotes, AssignmentExpression is an *expression*, which
# means at the module toplevel it is wrapped into an ExpressionStatement
# which is where the comments get attached
def enter_ExpressionStatement(self, node):
self.declaration = Declaration(comments=node.get('comments'))
def exit_ExpressionStatement(self, node):
self.declaration = None
def enter_AssignmentExpression(self, node):
target = node['left']
if target['type'] == 'Identifier':
self.declaration.id = target['name']
self.scope[self.declaration.id] = ValueExtractor(
self, self.declaration
).visit(node['right'])
self.declaration.id = None
return SKIP
if target['type'] != 'MemberExpression':
raise ValueError("Unhandled assign to %s" % target['type'])
# only assign to straight a.b.c patterns (OK and trivial literals)
if target['computed'] and target['property']['type'] != 'Literal':
return SKIP
name = utils._value(target['property'], strict=True)
if isinstance(name, type(u'')) and name.endswith('.extend'):
return SKIP # ignore overwrite of .extend (WTF)
self.declaration.id = name
it = ValueExtractor(
self, self.declaration
).visit(node['right'])
self.declaration.id = None
assert it, "assigned a non-value from %s to %s" % (node['right'], name)
@self.result.post.append
def _augment_module(modules):
try:
t = deref(m2r(target['object'], self.scope.freeze()))
except ValueError:
return # f'n extension of global libraries garbage
if not isinstance(t, jsdoc.NSDoc):
# function Foo(){}; Foo.prototype = bar
# fuck that yo
return
# TODO: note which module added this
m = it
if isinstance(m, jsdoc.LiteralDoc):
m = jsdoc.PropertyDoc(m.to_dict())
t.add_member(name, m)
return SKIP
def enter_FunctionDeclaration(self, node):
""" Already processed by hoistitifier
"""
return SKIP
def enter_ReturnStatement(self, node):
self.declaration = Declaration(comments=node.get('comments'))
if node['argument']:
export = ValueExtractor(self, self.declaration).visit(node['argument'])
if isinstance(export, RefProxy):
self.module.parsed['exports'] = _name(export._ref)
self.scope['<exports>'] = export
self.declaration = None
return SKIP
def enter_CallExpression(self, node):
if utils.match(node, {
'callee': {
'type': 'MemberExpression',
'object': lambda n: (
n['type'] in ('Identifier', 'MemberExpression')
# _.str.include
and not utils._name(n).startswith('_')
),
'property': {'name': 'include'},
}
}):
target = RefProxy(m2r(node['callee']['object'], self.scope.freeze()))
target_name = utils._name(node['callee']['object'])
items = ClassProcessor(self).visit(node['arguments'])
@self.result.post.append
def resolve_extension(modules):
t = target.become(modules)
if not isinstance(t, jsdoc.ClassDoc):
return # FIXME: log warning
# raise ValueError("include() subjects should be classes, %s is %s" % (target_name, type(t)))
# TODO: note which module added these
for it in items:
if isinstance(it, dict):
for n, member in it.items():
t.add_member(n, member)
else:
t.parsed.setdefault('mixes', []).append(it.become(modules))
return SKIP
def refify(self, node, also=None):
it = m2r(node, self.scope.freeze())
assert isinstance(it, ref), "Expected ref, got {}".format(it)
px = RefProxy(it)
@self.result.post.append
def resolve(modules):
p = px.become(modules)
if also: also(p)
return px
class ValueExtractor(Visitor):
def __init__(self, parent, declaration=None):
super(ValueExtractor, self).__init__()
self.parent = parent
self.declaration = declaration or Declaration()
def enter_generic(self, node):
self.result = jsdoc.parse_comments(
self.declaration.comments,
jsdoc.Unknown.from_(node['type'])
)
self._update_result_meta()
return SKIP
def _update_result_meta(self, name=None):
self.result.parsed['sourcemodule'] = self.parent.module
n = name or self.declaration.id
if n:
self.result.set_name(n)
def enter_Literal(self, node):
self.result = jsdoc.parse_comments(
self.declaration.comments, jsdoc.LiteralDoc)
self._update_result_meta()
self.result.parsed['value'] = node['value']
return SKIP
def enter_Identifier(self, node):
self.result = self.parent.refify(node)
return SKIP
def enter_MemberExpression(self, node):
self.result = RefProxy(ref(
ValueExtractor(self.parent).visit(node['object']),
utils._value(node['property'], strict=True)
))
self.parent.result.post.append(self.result.become)
return SKIP
def enter_FunctionExpression(self, node):
name, comments = (self.declaration.id, self.declaration.comments)
self.result = jsdoc.parse_comments(comments, jsdoc.FunctionDoc)
self.result.parsed['name'] = node['id'] and node['id']['name']
self._update_result_meta()
self.result.parsed['guessed_params'] = [p['name'] for p in node['params']]
return SKIP
def enter_NewExpression(self, node):
comments = self.declaration.comments if self.declaration else node.get('comments')
self.result = ns = jsdoc.parse_comments(comments, jsdoc.InstanceDoc)
self._update_result_meta()
def _update_contents(cls):
if not isinstance(cls, jsdoc.ClassDoc):
return
ns.parsed['cls'] = cls
self.parent.refify(node['callee'], also=_update_contents)
return SKIP
def enter_ObjectExpression(self, node):
self.result = obj = jsdoc.parse_comments(self.declaration.comments)
self._update_result_meta()
for n, p in MemberExtractor(parent=self.parent).visit(node['properties']).items():
obj.add_member(n, p)
return SKIP
def enter_CallExpression(self, node):
# require(a_module_name)
if utils.match(node, {'callee': {'type': 'Identifier', 'name': self.parent.requirefunc}}):
depname = node['arguments'][0]['value']
# TODO: clean this up
self.parent.result.dependencies.add(depname)
self.result = ModuleProxy(name=depname)
self.parent.result.post.append(self.result.become)
# Class.extend(..mixins, {})
elif utils.match(node, {
'callee': {
'type': 'MemberExpression',
'object': lambda n: (
n['type'] in ('Identifier', 'MemberExpression')
# $.extend, $.fn.extend, _.extend
and not utils._name(n).startswith(('_', '$'))
),
'property': {'name': 'extend'},
},
}): # creates a new class, but may not actually return it
obj = node['callee']['object']
comments = self.declaration.comments
self.result = cls = jsdoc.parse_comments(comments, jsdoc.ClassDoc)
cls.parsed['extends'] = self.parent.refify(obj)
self._update_result_meta()
items = ClassProcessor(self.parent).visit(node['arguments'])
@self.parent.result.post.append
def add_to_class(modules):
for item in items:
if isinstance(item, dict):
# methods/attributes
for n, method in item.items():
cls.add_member(n, method)
else:
cls.parsed.setdefault('mixes', []).append(item)
# other function calls
else:
self.result = jsdoc.parse_comments(self.declaration.comments, jsdoc.guess)
self._update_result_meta()
return SKIP
class ClassProcessor(Visitor):
def __init__(self, parent):
super(ClassProcessor, self).__init__()
self.result = []
self.parent = parent
def enter_generic(self, node):
self.result.append(self.parent.refify(node))
return SKIP
def enter_ObjectExpression(self, node):
self.result.append(MemberExtractor(parent=self.parent, for_class=True).visit(node['properties']))
return SKIP
String = type(u'')
class MemberExtractor(Visitor):
def __init__(self, parent, for_class=False):
super(MemberExtractor, self).__init__()
self.result = collections.OrderedDict()
self.parent = parent
self.for_class = for_class
def enter_Property(self, node):
name = utils._value(node['key'])
prop = ValueExtractor(
self.parent,
Declaration(id=name, comments=node.get('comments'))
).visit(node['value'])
if isinstance(prop, jsdoc.LiteralDoc):
prop = jsdoc.PropertyDoc(prop.to_dict())
# ValueExtractor can return a Ref, maybe this should be sent as part
# of the decl/comments?
if name.startswith('_') and hasattr(prop, 'parsed'):
prop.parsed['private'] = True
self.result[name] = prop
return SKIP
class Hoistifier(Visitor):
"""
Processor for variable and function declarations properly hoisting them
to the "top" of a module such that they are available with the relevant
value afterwards.
"""
def __init__(self, parent):
super(Hoistifier, self).__init__()
self.parent = parent
def enter_generic(self, node):
return SKIP
# nodes to straight recurse into, others are just skipped
enter_BlockStatement = enter_VariableDeclaration = lambda self, node: None
def enter_VariableDeclarator(self, node):
self.parent.scope[node['id']['name']] = BASE_SCOPE['undefined']
def enter_FunctionDeclaration(self, node):
funcname = node['id']['name']
self.parent.scope[funcname] = fn = jsdoc.parse_comments(
node.get('comments'),
jsdoc.FunctionDoc,
)
fn.parsed['sourcemodule'] = self.parent.module
fn.parsed['name'] = funcname
fn.parsed['guessed_params'] = [p['name'] for p in node['params']]
return SKIP
class TypedefMatcher(Visitor):
def __init__(self, parent):
super(TypedefMatcher, self).__init__()
self.parent = parent
self.result = []
enter_BlockStatement = lambda self, node: None
def enter_generic(self, node):
# just traverse all top-level statements, check their comments, and
# bail
for comment in node.get('comments') or []:
if '@typedef' in comment['value']:
extract = '\n' + jsdoc.strip_stars('/*' + comment['value'] + '\n*/')
parsed = pyjsdoc.parse_comment(extract, u'')
p = jsdoc.ParamDoc(parsed['typedef'])
parsed['name'] = p.name
parsed['sourcemodule'] = self.parent.module
# TODO: add p.type as superclass somehow? Builtin types not in scope :(
self.result.append(jsdoc.ClassDoc(parsed))
return SKIP
@@ -1,6 +0,0 @@
:orphan:
These files should be run via pytest_, simply install pytest and from the top
of the Odoo project run ``pytest doc/_extensions``.
.. _pytest: https://pytest.org/
@@ -1,73 +0,0 @@
# -*- coding: utf-8 -*-
import operator
import pyjsparser
from autojsdoc.parser import jsdoc, parser
params = operator.attrgetter('name', 'type', 'doc')
def parse(s, source=None):
tree = pyjsparser.parse(s)
mods = parser.ModuleMatcher(source).visit(tree)
post(mods)
return mods
def post(mods):
modules = dict(BASE_MODULES)
modules.update((m.name, m) for m in mods)
for mod in mods:
mod.post_process(modules)
BASE_MODULES = {
'other': jsdoc.ModuleDoc({
'module': 'other',
'_members': [
('<exports>', jsdoc.LiteralDoc({'name': 'value', 'value': "ok"})),
],
}),
'dep2': jsdoc.ModuleDoc({
'module': 'dep2',
'_members': [
('<exports>', jsdoc.LiteralDoc({'value': 42.})),
],
}),
'dep3': jsdoc.ModuleDoc({
'module': 'dep3',
'_members': [
('<exports>', jsdoc.LiteralDoc({'value': 56.})),
],
}),
'Class': jsdoc.ModuleDoc({
'module': 'Class',
'_members': [
('<exports>', jsdoc.ClassDoc({
'name': 'Class',
'doc': "Base Class"
})),
],
}),
'mixins': jsdoc.ModuleDoc({
'module': 'mixins',
'_members': [
('<exports>', jsdoc.NSDoc({
'name': 'mixins',
'_members': [
('Bob', jsdoc.ClassDoc({'class': "Bob"})),
]
})),
],
}),
'Mixin': jsdoc.ModuleDoc({
'module': 'Mixin',
'_members': [
('<exports>', jsdoc.MixinDoc({
'_members': [
('a', jsdoc.FunctionDoc({'function': 'a'})),
]
})),
],
})
}
@@ -1,227 +0,0 @@
# -*- coding: utf-8 -*-
import pytest
from autojsdoc.parser import jsdoc
from support import params, parse
def test_classvar():
[mod] = parse("""
odoo.define('a.A', function(require) {
var Class = require('Class');
/**
* This is my class-kai
*/
var A = Class.extend({});
return A;
});
""")
cls = mod.exports
assert type(cls) == jsdoc.ClassDoc
assert type(cls.superclass) == jsdoc.ClassDoc
assert cls.superclass.name == 'Class'
assert cls.name == 'A'
assert cls.constructor is None
assert cls.properties == []
assert cls['doc'] == 'This is my class-kai'
def test_classret():
[mod] = parse("""
odoo.define('a.A', function(require) {
var Class = require('Class');
/**
* This is my class-kai
*/
return Class.extend({});
});
""")
cls = mod.exports
assert type(cls) == jsdoc.ClassDoc
assert cls.name == ''
assert cls.constructor is None
assert cls.properties == []
assert cls['doc'] == 'This is my class-kai'
def test_methods():
[mod] = parse("""
odoo.define('a.A', function(require) {
var Class = require('Class');
return Class.extend({
/**
* @param {Widget} parent
*/
init: function (parent) {},
/**
* @returns {Widget}
*/
itself: function () { return this; },
/**
* @param {MouseEvent} e
*/
_onValidate: function (e) {},
});
});
""")
cls = mod.exports
assert len(cls.properties) == 3
assert cls.constructor
# assume methods are in source order
[_, init] = cls.properties[0]
assert init == cls.constructor
assert init.name == 'init'
assert not init.is_private
assert init.is_constructor
[param] = init.params
assert params(param) == ('parent', 'Widget', '')
[_, itself] = cls.properties[1]
assert itself.name == 'itself'
assert not itself.is_private
assert not itself.is_constructor
assert not itself.params
assert params(itself.return_val) == ('', 'Widget', '')
[_, _on] = cls.properties[2]
assert _on.name == '_onValidate'
assert _on.is_private
assert not _on.is_constructor
[param] = _on.params
assert params(param) == ('e', 'MouseEvent', '')
def test_mixin_explicit():
[mod] = parse("""
odoo.define('a.A', function (require) {
var Class = require('Class');
var mixins = require('mixins');
/**
* This is my class-kai
* @mixes mixins.Bob
*/
return Class.extend({});
});
""")
cls = mod.exports
# FIXME: ClassDoc may want to m2r(mixin, scope)?
assert cls.mixins == ['mixins.Bob']
def test_mixin_implicit():
[mod] = parse("""
odoo.define('a.A', function(require) {
var Class = require('Class');
var Mixin = require('Mixin');
/**
* This is my class-kai
*/
return Class.extend(Mixin, { foo: function() {} });
});
""")
cls = mod.exports
[mixin] = cls.mixins
assert type(mixin) == jsdoc.MixinDoc
assert params(mixin.properties[0][1]) == ('a', 'Function', '')
assert params(mixin.get_property('a')) == ('a', 'Function', '')
assert params(cls.get_property('foo')) == ('foo', 'Function', '')
def test_instanciation():
[A, a] = parse("""
odoo.define('A', function (r) {
var Class = r('Class');
/**
* @class A
*/
return Class.extend({
foo: function () {}
});
});
odoo.define('a', function (r) {
var A = r('A');
var a = new A;
return a;
});
""")
assert type(a.exports) == jsdoc.InstanceDoc
assert a.exports.cls.name == A.exports.name
def test_non_function_properties():
[A] = parse("""
odoo.define('A', function (r) {
var Class = r('Class');
return Class.extend({
template: 'thing',
a_prop: [1, 2, 3],
'other': {a: 7}
});
});
""")
t = A.exports.get_property('template')
assert type(t) == jsdoc.PropertyDoc
assert params(t) == ('template', 'String', '')
assert not t.is_private
def test_non_extend_classes():
[mod] = parse("""
odoo.define('A', function () {
/**
* @class
*/
var Class = function () {}
return Class;
});
""")
assert type(mod.exports) == jsdoc.ClassDoc
def test_extend():
[a, _] = parse("""
odoo.define('A', function (require) {
var Class = require('Class');
return Class.extend({});
});
odoo.define('B', function (require) {
var A = require('A');
A.include({
/** A property */
a: 3,
/** A method */
b: function () {}
});
});
""")
cls = a.exports
assert type(cls) == jsdoc.ClassDoc
a = cls.get_property('a')
assert type(a) == jsdoc.PropertyDoc
assert params(a) == ('a', 'Number', 'A property')
b = cls.get_property('b')
assert type(b) == jsdoc.FunctionDoc
assert params(b) == ('b', 'Function', 'A method')
# TODO: also support virtual members?
# TODO: computed properties?
@pytest.mark.skip(reason="Need to implement member/var-parsing?")
def test_members():
[mod] = parse("""
odoo.define('A', function (r) {
var Class = r('Class');
return Class.extend({
init: function () {
/**
* This is bob
* @var {Foo}
*/
this.foo = 3;
this.bar = 42;
/**
* @member {Baz}
*/
this.baz = null;
}
});
});
""")
cls = mod.exports
assert params(cls.members[0]) == ('foo', 'Foo', 'This is bob')
assert params(cls.members[1]) == ('bar', '', '')
assert params(cls.members[2]) == ('baz', 'Baz', '')
@@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
"""
Test various crap patterns found in Odoo code to ensure they don't blow up
the parser thingie
"""
from autojsdoc.parser import jsdoc
from support import parse
def test_export_external():
[mod] = parse("""
odoo.define('module', function () {
return $.Deferred().reject();
});
""")
assert isinstance(mod.exports, jsdoc.CommentDoc)
assert mod.exports.doc == ''
def test_extend_jq():
parse("""
odoo.define('a', function (r) {
$.extend($.expr[':'], { a: function () {} });
$.fn.extend({ a: function () {} });
});
""")
def test_extend_dynamic():
parse("""
odoo.define('a', function () {
foo.bar.baz[qux + '_external'] = function () {};
});
""")
def test_extend_deep():
parse("""
odoo.define('a', function () {
var eventHandler = $.summernote.eventHandler;
var dom = $.summernote.core.dom;
dom.thing = function () {};
var fn_editor_currentstyle = eventHandler.modules.editor.currentStyle;
eventHandler.modules.editor.currentStyle = function () {}
});
""")
def test_arbitrary():
parse("""
odoo.define('bob', function () {
var page = window.location.href.replace(/^.*\/\/[^\/]+/, '');
var mailWidgets = ['mail_followers', 'mail_thread', 'mail_activity', 'kanban_activity'];
var bob;
var fldj = foo.getTemplate().baz;
});
""")
def test_prototype():
[A, B] = parse("""
odoo.define('mod1', function () {
var exports = {};
exports.Foo = Backbone.Model.extend({});
exports.Bar = Backbone.Model.extend({});
var BarCollection = Backbone.Collection.extend({
model: exports.Bar,
});
exports.Baz = Backbone.Model.extend({});
return exports;
});
odoo.define('mod2', function (require) {
var models = require('mod1');
var _super_orderline = models.Bar.prototype;
models.Foo = models.Bar.extend({});
var _super_order = models.Baz.prototype;
models.Bar = models.Baz.extend({});
});
""")
@@ -1,173 +0,0 @@
# -*- coding: utf-8 -*-
from autojsdoc.parser import jsdoc
from support import params, parse, BASE_MODULES
def test_single():
[mod] = parse("""
/**
* This is a super module!
*/
odoo.define('supermodule', function (req) {
var other = req('other');
});
""")
assert mod.name == 'supermodule'
assert mod.dependencies == {'other'}
assert mod.exports is None
assert mod.doc == "This is a super module!"
m = mod.get_property('other')
assert m.name == 'value', "property is exported value not imported module"
def test_multiple():
[mod1, mod2, mod3] = parse("""
odoo.define('module1', function (req) {
return 1;
});
odoo.define('module2', function (req) {
return req('dep2');
});
odoo.define('module3', function (req) {
var r = req('dep3');
return r;
});
""")
assert mod1.name == 'module1'
assert mod1.dependencies == set()
assert isinstance(mod1.exports, jsdoc.LiteralDoc)
assert mod1.exports.value == 1.0
assert mod1.exports['sourcemodule'] is mod1
assert mod1.doc == ""
assert mod2.name == 'module2'
assert mod2.dependencies == {'dep2'}
assert isinstance(mod2.exports, jsdoc.LiteralDoc)
assert mod2.exports.value == 42.0
assert mod2.doc == ""
assert mod3.name == 'module3'
assert mod3.dependencies == {'dep3'}
assert isinstance(mod3.exports, jsdoc.LiteralDoc)
assert mod3.exports.value == 56.0
assert mod3.doc == ''
def test_func():
[mod] = parse("""
odoo.define('module', function (d) {
/**
* @param {Foo} bar this is a bar
* @param {Baz} qux this is a qux
*/
return function (bar, qux) {
return 42;
}
});
""")
exports = mod.exports
assert type(exports) == jsdoc.FunctionDoc
assert exports['sourcemodule'] is mod
assert exports.name == ''
assert exports.is_constructor == False
assert exports.is_private == False
assert params(exports.params[0]) == ('bar', 'Foo', 'this is a bar')
assert params(exports.params[1]) == ('qux', 'Baz', 'this is a qux')
assert params(exports.return_val) == ('', '', '')
def test_hoist():
[mod] = parse("""
odoo.define('module', function() {
return foo;
/**
* @param a_thing
*/
function foo(a_thing) {
return 42;
}
});
""")
actual = mod.exports
assert type(actual) == jsdoc.FunctionDoc
[param] = actual.params
assert params(param) == ('a_thing', '', '')
def test_export_instance():
[mod] = parse("""
odoo.define('module', function (require) {
var Class = require('Class');
/**
* Provides an instance of Class
*/
return new Class();
});
""")
assert type(mod.exports) == jsdoc.InstanceDoc
assert mod.exports.doc == 'Provides an instance of Class'
assert mod.exports['sourcemodule'] is mod
def test_bounce():
[m2, m1] = parse("""
odoo.define('m2', function (require) {
var Item = require('m1');
return {
Item: Item
};
});
odoo.define('m1', function (require) {
var Class = require('Class');
var Item = Class.extend({});
return Item;
});
""")
assert type(m2.exports) == jsdoc.NSDoc
it = m2.exports.get_property('Item')
assert type(it) == jsdoc.ClassDoc
assert it['sourcemodule'] is m1
assert sorted([n for n, _ in m1.properties]) == ['<exports>', 'Class', 'Item']
def test_reassign():
[m] = parse("""
odoo.define('m', function (require) {
var Class = require('Class');
/** local class */
var Class = Class.extend({});
return Class
});
""")
assert m.exports.doc == 'local class'
# can't use equality or identity so use class comment...
assert m.exports.superclass.doc == 'Base Class'
def test_attr():
[m1, m2] = parse("""
odoo.define('m1', function (require) {
var Class = require('Class');
var Item = Class.extend({});
return {Item: Item};
});
odoo.define('m2', function (require) {
var Item = require('m1').Item;
Item.include({});
return Item.extend({});
});
""")
assert type(m2.exports) == jsdoc.ClassDoc
# that these two are resolved separately may be an issue at one point (?)
assert m2.exports.superclass.to_dict() == m1.exports.get_property('Item').to_dict()
def test_nothing_implicit():
[m] = parse("""
odoo.define('m', function () {
});
""")
assert m.exports is None
def test_nothing_explicit():
[m] = parse("""
odoo.define('m', function () {
return;
});
""")
assert m.exports is None
@@ -1,177 +0,0 @@
# -*- coding: utf-8 -*-
from autojsdoc.parser import jsdoc
from support import parse, params
def test_empty():
[mod] = parse("""
odoo.define('a.ns', function (r) {
return {};
});
""")
assert type(mod.exports) == jsdoc.NSDoc
assert mod.exports.properties == []
def test_inline():
[mod] = parse("""
odoo.define('a.ns', function (r) {
return {
/**
* a thing
* @type {Boolean}
*/
a: true
}
});
""")
assert isinstance(mod.exports, jsdoc.NSDoc)
[(n, p)] = mod.exports.properties
assert n == 'a'
assert params(p) == ('a', 'Boolean', 'a thing')
def test_header():
[mod] = parse("""
odoo.define('a.ns', function (r) {
/**
* @property {Boolean} a a thing
*/
return { a: true }
});
""")
assert isinstance(mod.exports, jsdoc.NSDoc)
[(_, p)] = mod.exports.properties
assert params(p) == ('a', 'Boolean', 'a thing')
def test_header_conflict():
""" should the header or the inline comment take precedence? """
[mod] = parse("""
odoo.define('a.ns', function (r) {
/**
* @property {Boolean} a a thing
*/
return {
/** @type {String} */
a: true
}
});
""")
assert isinstance(mod.exports, jsdoc.NSDoc)
[(_, p)] = mod.exports.properties
assert params(p) == ('a', 'Boolean', 'a thing')
def test_mixin():
[mod] = parse("""
odoo.define('a.mixin', function (r) {
/**
* @mixin
*/
return {
/**
* @returns {Number} a number
*/
do_thing: function other() { return 42; }
}
});
""")
assert isinstance(mod.exports, jsdoc.MixinDoc)
[(n, p)] = mod.exports.properties
assert n == 'do_thing'
assert params(p) == ('other', 'Function', '')
assert params(p.return_val) == ('', 'Number', 'a number')
def test_literal():
[mod] = parse("""
odoo.define('a.ns', function (r) {
/** whop whop */
return {
'a': 1,
/** wheee */
'b': 2,
};
});
""")
assert mod.exports.doc == 'whop whop'
[(_1, a), (_2, b)] = mod.exports.properties
assert params(a) == ('a', 'Number', '')
assert params(b) == ('b', 'Number', 'wheee')
def test_fill_ns():
[mod] = parse("""
odoo.define('a.ns', function (r) {
var Class = r('Class');
var ns = {};
/** ok */
ns.a = 1;
/** @type {String} */
ns['b'] = 2;
/** Ike */
ns.c = Class.extend({});
ns.d = function () {}
return ns;
});
""")
ns = mod.exports
assert type(ns) == jsdoc.NSDoc
[(_a, a), (_b, b), (_c, c), (_d, d)] = ns.properties
assert params(a) == ('a', 'Number', 'ok')
assert params(b) == ('b', 'String', '')
assert type(c) == jsdoc.ClassDoc
assert type(d) == jsdoc.FunctionDoc
def test_extend_other():
[o, b] = parse("""
odoo.define('a.ns', function () {
/** @name outer */
return {
/** @name inner */
a: {}
};
});
odoo.define('b', function (r) {
var o = r('a.ns');
var Class = r('Class');
/** Class 1 */
o.a.b = Class.extend({m_b: 1});
/** Class 2 */
o.a['c'] = Class.extend({m_c: 1});
});
""")
[(_, m)] = o.exports.properties
assert type(m) == jsdoc.NSDoc
b = m.get_property('b')
assert type(b) == jsdoc.ClassDoc
assert b.get_property('m_b')
assert b.doc == 'Class 1'
c = m.get_property('c')
assert type(c) == jsdoc.ClassDoc
assert c.get_property('m_c')
assert c.doc == 'Class 2'
def test_ns_variables():
[mod] = parse("""
odoo.define('A', function (r) {
var Class = r('Class');
var Thing = Class.extend({});
return {
Thing: Thing
};
});
""")
p = mod.exports.get_property('Thing')
assert type(p) == jsdoc.ClassDoc
def test_diff():
""" Have the NS key and the underlying object differ
"""
[mod] = parse("""
odoo.define('mod', function (r) {
var Class = r('Class');
var Foo = Class.extend({});
return { Class: Foo };
});
""")
c = mod.exports.get_property('Class')
assert type(c) == jsdoc.ClassDoc
assert c.name == 'Foo'
@@ -1,116 +0,0 @@
# -*- coding: utf-8 -*-
from autojsdoc.parser.jsdoc import ParamDoc
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
def test_basic():
d = ParamDoc("Lorem ipsum dolor sit amet, consectetur adipiscing elit.").to_dict()
assert d == {
'name': 'Lorem',
'type': '',
'optional': False,
'default': None,
'doc': 'ipsum dolor sit amet, consectetur adipiscing elit.',
}
d = ParamDoc("{Lorem} ipsum dolor sit amet, consectetur adipiscing elit.").to_dict()
assert d == {
'name': 'ipsum',
'type': 'Lorem',
'optional': False,
'default': None,
'doc': 'dolor sit amet, consectetur adipiscing elit.',
}
def test_optional():
d = ParamDoc("[Lorem] ipsum dolor sit amet, consectetur adipiscing elit.").to_dict()
assert d == {
'name': 'Lorem',
'type': '',
'optional': True,
'default': None,
'doc': 'ipsum dolor sit amet, consectetur adipiscing elit.',
}
d = ParamDoc("[Lorem=42] ipsum dolor sit amet, consectetur adipiscing elit.").to_dict()
assert d == {
'name': 'Lorem',
'type': '',
'optional': True,
'default': "42",
'doc': 'ipsum dolor sit amet, consectetur adipiscing elit.',
}
d = ParamDoc("{Lorem} [ipsum] dolor sit amet, consectetur adipiscing elit.").to_dict()
assert d == {
'name': 'ipsum',
'type': 'Lorem',
'optional': True,
'default': None,
'doc': 'dolor sit amet, consectetur adipiscing elit.',
}
d = ParamDoc("{Lorem} [ipsum=42] dolor sit amet, consectetur adipiscing elit.").to_dict()
assert d == {
'name': 'ipsum',
'type': 'Lorem',
'optional': True,
'default': '42',
'doc': 'dolor sit amet, consectetur adipiscing elit.',
}
def test_returns():
d = ParamDoc("{} ipsum dolor sit amet, consectetur adipiscing elit.").to_dict()
assert d == {
'name': '',
'type': '',
'optional': False,
'default': None,
'doc': 'ipsum dolor sit amet, consectetur adipiscing elit.',
}
d = ParamDoc("{Lorem} ipsum dolor sit amet, consectetur adipiscing elit.").to_dict()
assert d == {
'name': '',
'type': 'Lorem',
'optional': False,
'default': None,
'doc': 'ipsum dolor sit amet, consectetur adipiscing elit.',
}
def test_odd():
d = ParamDoc("{jQuery} [$target] the node where content will be prepended").to_dict()
assert d == {
'name': '$target',
'type': 'jQuery',
'optional': True,
'default': None,
'doc': 'the node where content will be prepended',
}
d = ParamDoc("""{htmlString} [content] DOM element,
array of elements, HTML string or jQuery object to prepend to $target""").to_dict()
assert d == {
'name': 'content',
'type': 'htmlString',
'optional': True,
'default': None,
'doc': "DOM element,\n array of elements, HTML string or jQuery object to prepend to $target",
}
d = ParamDoc("{Boolean} [options.in_DOM] true if $target is in the DOM").to_dict()
assert d == {
'name': 'options.in_DOM',
'type': 'Boolean',
'optional': True,
'default': None,
'doc': 'true if $target is in the DOM',
}
d = ParamDoc("Lorem\n ipsum dolor sit amet, consectetur adipiscing elit.").to_dict()
assert d['doc'] == 'ipsum dolor sit amet, consectetur adipiscing elit.'
d = ParamDoc("Lorem - ipsum dolor sit amet, consectetur adipiscing elit.").to_dict()
assert d['doc'] == 'ipsum dolor sit amet, consectetur adipiscing elit.'
d = ParamDoc("Lorem : ipsum dolor sit amet, consectetur adipiscing elit.").to_dict()
assert d['doc'] == 'ipsum dolor sit amet, consectetur adipiscing elit.'
@@ -1,104 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import pytest
from autojsdoc.parser import types
def test_parser():
assert types.parse("MouseEvent|TouchEvent") == types.Alt([
types.Type('MouseEvent', []),
types.Type('TouchEvent', []),
])
assert types.parse('Deferred<Object>') == types.Alt([
types.Type('Deferred', [
types.Alt([types.Type('Object', [])])
])
])
assert types.parse('Object|undefined') == types.Alt([
types.Type('Object', []),
types.Literal('undefined'),
])
assert types.parse('any[]') == types.Alt([
types.Type('Array', [types.Type('any', [])])
])
assert types.parse("'xml' | 'less'") == types.Alt([
types.Literal('xml'),
types.Literal('less'),
])
assert types.parse('Foo<Bar | Bar[] | null>') == types.Alt([
types.Type('Foo', [types.Alt([
types.Type('Bar', []),
types.Type('Array', [types.Type('Bar', [])]),
types.Literal('null'),
])])
])
assert types.parse('Function<Array<Object[]>>') == types.Alt([
types.Type('Function', [types.Alt([
types.Type('Array', [types.Alt([
types.Type('Array', [
types.Type('Object', [])
])
])])
])])
])
def test_tokens():
toks = list(types.tokenize('A'))
assert toks == [(types.NAME, 'A')]
toks = list(types.tokenize('"foo" | "bar"'))
assert toks == [(types.LITERAL, 'foo'), (types.OP, '|'), (types.LITERAL, 'bar')]
toks = list(types.tokenize('1 or 2'))
assert toks == [(types.LITERAL, 1), (types.OP, '|'), (types.LITERAL, 2)]
toks = list(types.tokenize('a.b.c.d'))
assert toks == [
(types.NAME, 'a'), (types.OP, '.'),
(types.NAME, 'b'), (types.OP, '.'),
(types.NAME, 'c'), (types.OP, '.'),
(types.NAME, 'd'),
]
toks = list(types.tokenize('Function<String, Object>'))
assert toks == [
(types.NAME, 'Function'),
(types.OP, '<'),
(types.NAME, 'String'),
(types.OP, ','),
(types.NAME, 'Object'),
(types.OP, '>')
]
toks = list(types.tokenize('Function<Array<Object[]>>'))
assert toks == [
(types.NAME, 'Function'),
(types.OP, '<'),
(types.NAME, 'Array'),
(types.OP, '<'),
(types.NAME, 'Object'),
(types.OP, '['),
(types.OP, ']'),
(types.OP, '>'),
(types.OP, '>')
]
def test_peekable():
p = types.Peekable(range(5))
assert p.peek() == 0
assert next(p) == 0, "consuming should yield previously peeked value"
next(p)
next(p)
assert next(p) == 3
assert p.peek() == 4
next(p)
assert p.peek(None) is None
with pytest.raises(StopIteration):
p.peek()
with pytest.raises(StopIteration):
next(p)
-196
View File
@@ -1,196 +0,0 @@
# -*- coding: utf-8 -*-
import ast
import collections
import io
import token
from tokenize import generate_tokens
LITERAL_NAME = ('null', 'undefined', 'true', 'false')
__all__ = ['tokenize', 'parse', 'iterate', 'Alt', 'Type', 'Literal']
class _Marker(object):
__slots__ = ['name']
def __init__(self, name): self.name = name
def __repr__(self): return '<%s>' % self.name
OP = _Marker('OP')
NAME = _Marker('NAME')
LITERAL = _Marker('LITERAL')
def tokenize(typespec):
typespec = typespec.replace('$', 'jQuery')
for toktype, string, _, _, _ in generate_tokens(io.StringIO(typespec).readline):
if toktype == token.NAME:
if string == 'or': # special case "A or B"
# TODO: deprecation warning
yield (OP, '|')
else:
yield (NAME, string)
elif toktype == token.OP:
if string in '|<>[].,':
yield (OP, string)
elif string == '>>':
yield (OP, '>')
yield (OP, '>')
elif string == '*': # maybe?
yield (NAME, 'any')
elif string in '()':
# TODO: deprecation warning
# seems useless unless we add support for "..."
continue
else:
raise ValueError("Unknown typespec operator %r" % string)
elif toktype in (token.STRING, token.NUMBER): # enum-ish
yield (LITERAL, ast.literal_eval(string))
elif toktype == token.ENDMARKER:
return
elif toktype == token.NEWLINE:
pass
else:
raise ValueError("Unknown typespec token %s" % token.tok_name[toktype])
Alt = collections.namedtuple('Alt', ['params'])
Literal = collections.namedtuple('Literal', ['value'])
Type = collections.namedtuple('Type', ['name', 'params'])
def iterate(t, fortype, forliteral):
if isinstance(t, Alt):
ps = Peekable(t.params)
for param in ps:
for it in iterate(param, fortype, forliteral):
yield it
if ps.peek(None):
yield forliteral('|')
elif isinstance(t, Type):
yield fortype(t.name)
if not t.params:
return
yield forliteral('<')
ps = Peekable(t.params)
for param in ps:
for it in iterate(param, fortype, forliteral):
yield it
if ps.peek(None):
yield forliteral(',')
yield forliteral('>')
elif isinstance(t, Literal):
yield forliteral(t.value)
else:
raise ValueError('Unknown item %s' % t)
def parse(typespec):
tokens = Peekable(tokenize(typespec))
return parse_alt(tokens)
# alt = param
# | param '|' alt
def parse_alt(tokens):
alt = Alt([])
while True:
alt.params.append(parse_param(tokens))
if not next_is(tokens, (OP, '|')):
break
return alt
# param = 'null' | 'undefined' | 'true' | 'false'
# | number
# | string
# | typename '[]'
# X '[' alt ']'
# | typename '<' params '>'
# params = alt | alt ',' params
def parse_param(tokens):
t, v = tokens.peek()
if t == LITERAL or (t == NAME and v in LITERAL_NAME):
next(tokens)
return Literal(str(v))
# # [typespec] # should this be Array<T> or an n-uple allowing multiple items?
# if tok == (OP, '['):
# next(tokens)
# t = Type('Array', [parse_alt(tokens)])
# rest = next(tokens)
# expect(rest, (OP, ']'))
# return t
t = Type(parse_typename(tokens), [])
# type[]
peek = tokens.peek(None)
if peek == (OP, '['):
next(tokens) # skip peeked
expect(next(tokens), (OP, ']'))
return Type('Array', [t])
# type<typespec, ...>
if peek == (OP, '<'):
next(tokens) # skip peeked
while True:
t.params.append(parse_alt(tokens))
n = next(tokens)
if n == (OP, ','):
continue
if n == (OP, '>'):
break
raise ValueError("Expected OP ',' or OP ',', got %s '%s'" % n)
return t
# typename = name | name '.' typename
def parse_typename(tokens):
typename = []
while True:
(t, n) = next(tokens)
if t != NAME:
raise ValueError("Expected a name token, got %s '%s'" % (t, n))
if n in LITERAL_NAME:
raise ValueError("Expected a type name, got literal %s" % n)
typename.append(n)
if not next_is(tokens, (OP, '.')):
break
return '.'.join(typename)
def next_is(tokens, expected):
"""
Consumes the next token if it's `expected` otherwise does not touch the
tokens.
Returns whether it consumed a token
"""
if tokens.peek(None) == expected:
next(tokens)
return True
return False
def expect(actual, expected):
""" Raises ValueError if `actual` and `expected` are different
:type actual: (object, str)
:type expected: (object, str)
"""
if actual != expected:
raise ValueError("Expected %s '%s', got %s '%s'" % (expected + actual))
NONE = object()
class Peekable(object):
__slots__ = ['_head', '_it']
def __init__(self, iterable):
self._head = NONE
self._it = iter(iterable)
def __iter__(self):
return self
def __next__(self):
if self._head is not NONE:
r, self._head = self._head, NONE
return r
return next(self._it)
next = __next__
def peek(self, default=NONE):
if self._head is NONE:
try:
self._head = next(self._it)
except StopIteration:
if default is not NONE:
return default
raise
return self._head
-295
View File
@@ -1,295 +0,0 @@
# -*- coding: utf-8 -*-
def _name(node):
if node['type'] == 'Identifier':
return node['name']
if node['type'] == 'MemberExpression':
return "%s.%s" % (_name(node['object']), _name(node['property']))
raise ValueError("Unnamable node type %s" % node['type'])
def _value(node, strict=False):
t = node['type']
if t == 'Identifier':
return node['name']
elif t == 'Literal':
return node['value']
msg = '<%s has no value>' % _identify(node, {})
if strict:
raise ValueError(msg)
return msg
def _identify(valnode, ns):
if valnode is None:
return "None"
# already identified and re-set in the ns?
if isinstance(valnode, Printable):
return valnode
# check other non-empty returns
t = valnode['type']
if t == "Literal":
if valnode.get('regex'):
return valnode['regex']['pattern']
return valnode['value']
elif t == "Identifier":
n = valnode['name']
return ns.get(n, Global(n))
elif t == "NewExpression":
return Instance(_identify(valnode['callee'], ns))
elif t == "ObjectExpression":
return Namespace({
_value(prop['key']): _identify(prop['value'], ns)
for prop in valnode['properties']
})
elif t == "MemberExpression":
return Deref(
_identify(valnode['object'], ns),
_identify(valnode['property'], ns) if valnode['computed'] else valnode['property']['name'],
)
elif t == "CallExpression":
return Call(
_identify(valnode['callee'], ns),
[_identify(arg, ns) for arg in valnode['arguments']],
)
elif t == 'BinaryExpression':
return "%s %s %s" % (
_identify(valnode['left'], ns),
valnode['operator'],
_identify(valnode['right'], ns),
)
else:
return t
class Counter(object):
__slots__ = ['_number']
def __init__(self):
self._number = 0
def __bool__(self):
return bool(self._number)
__nonzero__ = __bool__
def __int__(self):
return self._number
def get(self):
return self._number
def increment(self):
self._number += 1
return self._number
def decrement(self):
self._number -= 1
return self._number
def __enter__(self):
self._number += 1
return self._number
def __exit__(self, *args):
self._number -= 1
class Printable(object):
__slots__ = []
depth = Counter()
def __repr__(self):
with self.depth as i:
if i > 2:
return "%s(...)" % type(self).__name__
return "%s(%s)" % (type(self).__name__, ', '.join(
"%s=%r" % (k, getattr(self, k))
for k in self.__slots__
if not k.startswith('_')
if getattr(self, k)
))
def resolve(obj, store):
if obj is None:
return None
if isinstance(obj, (type(u''), bool, int, float)):
return obj
if isinstance(obj, ModuleProxy):
return resolve(obj.get(), store)
try:
if getattr(obj, 'resolved', False):
return obj
return obj.resolve(store)
except AttributeError:
raise TypeError("Unresolvable {!r}".format(obj))
class Resolvable(Printable):
"""
For types resolved in place
"""
__slots__ = ['resolved']
def __init__(self):
super(Resolvable, self).__init__()
self.resolved = False
def resolve(self, store):
self.resolved = True
return self
class Namespace(Resolvable):
__slots__ = ['attrs']
def __init__(self, attrs):
super(Namespace, self).__init__()
self.attrs = attrs
def resolve(self, store):
r = super(Namespace, self).resolve(store)
self.attrs = {
k: resolve(v, store)
for k, v in self.attrs.items()
}
return r
def __getitem__(self, key):
assert isinstance(key, type(u'')), "%r is not a namespace key" % key
try:
return self.attrs[key]
except KeyError:
return Global(self)[key]
class Module(Resolvable):
__slots__ = ('name', 'comments', 'exports', 'augments', 'dependencies', '_store')
def __init__(self, name, store, comments=()):
super(Module, self).__init__()
self.name = name
self._store = store
self.comments = tuple(comments)
self.exports = None
self.augments = []
self.dependencies = set()
store[name] = self
def add_dependency(self, depname):
dep = ModuleProxy(depname, self._store)
self.dependencies.add(dep)
return dep
def get(self):
return self
def resolve(self, store=None):
r = super(Module, self).resolve(store)
self.exports = resolve(self.exports, self._store)
self.augments = [resolve(a, self._store) for a in self.augments]
self.dependencies = {
resolve(d, self._store)
for d in self.dependencies
}
return r
def __getitem__(self, k):
try:
return self.exports[k]
except KeyError:
return Global(self)[k]
class ModuleProxy(object):
def __init__(self, name, store):
self.name = name
self.store = store
def get(self):
# web.web_client is not an actual module
return self.store.get(self.name, '<unstored %s>' % self.name)
def __repr__(self):
return repr(self.get())
def __hash__(self):
return hash(self.name)
def __eq__(self, other):
return self.name == other.name
class Class(Resolvable):
__slots__ = ['name', 'members', 'extends', 'mixins', 'comments']
def __init__(self, name, extends):
super(Class, self).__init__()
self.name = name
self.extends = extends
self.members = {}
self.mixins = []
self.comments = []
def resolve(self, store):
r = super(Class, self).resolve(store)
self.extends = resolve(self.extends, store)
self.mixins = [resolve(m, store) for m in self.mixins]
return r
class Instance(Resolvable):
__slots__ = ['type']
def __init__(self, type):
super(Instance, self).__init__()
self.type = type
def resolve(self, store):
r = super(Instance, self).resolve(store)
self.type = resolve(self.type, store)
if isinstance(self.type, Module):
self.type = self.type.exports
return r
def __getitem__(self, key):
return Global(self)['key']
class Function(Resolvable):
__slots__ = ['comments']
def __init__(self, comments):
super(Function, self).__init__()
self.comments = comments
class Deref(Printable):
__slots__ = ['object', 'property']
def __init__(self, object, property):
self.object = object
self.property = property
def resolve(self, store):
return resolve(self.object, store)[self.property]
class Call(Resolvable):
__slots__ = ['callable']
def __init__(self, callable, arguments):
super(Call, self).__init__()
self.callable = callable
def resolve(self, store):
r = super(Call, self).resolve(store)
self.callable = resolve(self.callable, store)
return r
def __getitem__(self, item):
return Global(self)[item]
def get(node, it, *path):
if not path:
return node[it]
return get(node[it], *path)
def match(node, pattern):
"""Checks that ``pattern`` is a subset of ``node``.
If a sub-pattern is a callable it will be called with the current
sub-node and its result is the match's. Other sub-patterns are
checked by equality against the correspoding sub-node.
Can be used to quickly check the descendants of a node of suitable
type.
TODO: support checking list content? Would be convenient to
constraint e.g. CallExpression based on their parameters
"""
if node is None and pattern is not None:
return
if callable(pattern):
return pattern(node)
if isinstance(node, list):
return all(len(node) > i and match(node[i], v) for i, v in pattern.items())
if isinstance(pattern, dict):
return all(match(node.get(k), v) for k, v in pattern.items())
return node == pattern
class Global(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return '<external %s>' % self.name
def __getitem__(self, name):
return Global('%s.%s' % (self.name, name))
def resolve(self, store):
return self
-126
View File
@@ -1,126 +0,0 @@
from pyjsparser.pyjsparserdata import Syntax
_binary = lambda n: [n['left'], n['right']]
_function = lambda n: n['params'] + n['defaults'] + [n['body']]
# yields children in visitation order
_children = {
Syntax.ArrayExpression: lambda n: n['elements'],
Syntax.ArrayPattern: lambda n: n['elements'],
Syntax.ArrowFunctionExpression: _function,
Syntax.AssignmentExpression: _binary,
Syntax.AssignmentPattern: _binary,
Syntax.BinaryExpression: _binary,
Syntax.BlockStatement: lambda n: n['body'],
Syntax.BreakStatement: lambda n: [],
Syntax.CallExpression: lambda n: [n['callee']] + n['arguments'],
Syntax.CatchClause: lambda n: [n['param'], n['body']],
Syntax.ClassBody: lambda n: [n['body']],
Syntax.ClassDeclaration: lambda n: [n['superClass'], n['body']],
Syntax.ClassExpression: lambda n: [n['superClass'], n['body']],
Syntax.ConditionalExpression: lambda n: [n['test'], n['consequent'], n['alternate']],
Syntax.ContinueStatement: lambda n: [],
Syntax.DebuggerStatement: lambda n: [],
Syntax.DoWhileStatement: lambda n: [n['body'], n['test']],
Syntax.EmptyStatement: lambda n: [],
Syntax.ExportAllDeclaration: lambda n: [n['source']],
Syntax.ExportDefaultDeclaration: lambda n: [n['declaration']],
Syntax.ExportNamedDeclaration: lambda n: ([n['declaration']] if n['declaration'] else n['specifiers']) + [n['source']],
Syntax.ExportSpecifier: lambda n: [n['local'], n['exported']],
Syntax.ExpressionStatement: lambda n: [n['expression']],
Syntax.ForStatement: lambda n: [n['init'], n['test'], n['update'], n['body']],
Syntax.ForInStatement: lambda n: [n['left'], n['right'], n['body']],
Syntax.FunctionDeclaration: _function,
Syntax.FunctionExpression: _function,
Syntax.Identifier: lambda n: [],
Syntax.IfStatement: lambda n: [n['test'], n['consequent'], n['alternate']],
Syntax.ImportDeclaration: lambda n: n['specifiers'] + [n['source']],
Syntax.ImportDefaultSpecifier: lambda n: [n['local']],
Syntax.ImportNamespaceSpecifier: lambda n: [n['local']],
Syntax.ImportSpecifier: lambda n: [n['local'], n['imported']],
Syntax.LabeledStatement: lambda n: [n['body']],
Syntax.Literal: lambda n: [],
Syntax.LogicalExpression: _binary,
Syntax.MemberExpression: lambda n: [n['object'], n['property']],
#Syntax.MethodDefinition: lambda n: [],
Syntax.NewExpression: lambda n: [n['callee']] + n['arguments'],
Syntax.ObjectExpression: lambda n: n['properties'],
Syntax.ObjectPattern: lambda n: n['properties'],
Syntax.Program: lambda n: n['body'],
Syntax.Property: lambda n: [n['key'], n['value']],
Syntax.RestElement: lambda n: [n['argument']],
Syntax.ReturnStatement: lambda n: [n['argument']],
Syntax.SequenceExpression: lambda n: n['expressions'],
Syntax.SpreadElement: lambda n: [n['argument']],
Syntax.Super: lambda n: [],
Syntax.SwitchCase: lambda n: [n['test'], n['consequent']],
Syntax.SwitchStatement: lambda n: [n['discriminant']] + n['cases'],
Syntax.TaggedTemplateExpression: lambda n: [n['tag'], n['quasi']],
Syntax.TemplateElement: lambda n: [],
Syntax.TemplateLiteral: lambda n: n['quasis'] + n['expressions'],
Syntax.ThisExpression: lambda n: [],
Syntax.ThrowStatement: lambda n: [n['argument']],
Syntax.TryStatement: lambda n: [n['block'], n['handler'], n['finalizer']],
Syntax.UnaryExpression: lambda n: [n['argument']],
Syntax.UpdateExpression: lambda n: [n['argument']],
Syntax.VariableDeclaration: lambda n: n['declarations'],
Syntax.VariableDeclarator: lambda n: [n['id'], n['init']],
Syntax.WhileStatement: lambda n: [n['test'], n['body']],
Syntax.WithStatement: lambda n: [n['object'], n['body']],
}
SKIP = object()
class Visitor(object):
"""
Generic visitor for the pyjsparser AST.
Visitation is driven by the ``visit`` method, which iterates the tree in
depth-first pre-order.
For each node, calls ``enter_$NODETYPE``, visits the children then calls
``exit_$NODETYPE``. If the enter or exit methods are not present on the
visitor, falls back on ``enter_generic`` and ``exit_generic``.
Any ``enter_`` method can return ``SKIP`` to suppress both the traversal
of the subtree *and* the call to the corresponding ``exit_`` method
(whether generic or specific).
For convenience, ``visit`` will return whatever is set as the visitor's
``result`` attribute, ``None`` by default.
``visit`` can be given multiple root nodes, and it can be called multiple
times. The ``result`` attribute is cleared at each call but not between
two roots of the same ``visit`` call.
"""
def __init__(self):
super(Visitor, self).__init__()
self.result = None
def enter_generic(self, node): pass
def exit_generic(self, node): pass
def visit(self, nodes):
if isinstance(nodes, dict):
nodes = [nodes]
# if multiple nodes are passed in, we need to reverse the order in
# order to traverse front-to-back rather than the other way around
nodes = list(reversed(nodes))
while nodes:
node = nodes.pop()
# should probably filter None descendants in _children...
if node is None:
continue
node_type = node['type']
if node_type == '_exit':
node = node['node']
getattr(self, 'exit_' + node['type'], self.exit_generic)(node)
continue
if getattr(self, 'enter_' + node_type, self.enter_generic)(node) is SKIP:
continue
nodes.append({'type': '_exit', 'node': node})
nodes.extend(reversed(_children[node_type](node)))
return self.result
View File
-24
View File
@@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
"""
Adds a new "exercise" admonition type
"""
def setup(app):
app.add_directive('exercise', Exercise)
app.add_node(exercise, html=(
lambda self, node: self.visit_admonition(node, 'exercise'),
lambda self, node: self.depart_admonition(node)
), latex=(
lambda self, node: self.visit_admonition(node),
lambda self, node: self.depart_admonition(node)
))
from docutils import nodes
from docutils.parsers.rst.directives import admonitions
class exercise(nodes.Admonition, nodes.Element): pass
class Exercise(admonitions.BaseAdmonition):
node_class = exercise
from sphinx.locale import admonitionlabels
admonitionlabels['exercise'] = 'Exercise'
-105
View File
@@ -1,105 +0,0 @@
import inspect
import importlib
import os.path
from werkzeug import urls
"""
* adds github_link(mode) context variable: provides URL (in relevant mode) of
current document on github
* if sphinx.ext.linkcode is enabled, automatically generates github linkcode
links (by setting config.linkcode_resolve)
Settings
========
* ``github_user``, username/organisation under which the project lives
* ``github_project``, name of the project on github
* (optional) ``version``, github branch to link to (default: master)
Notes
=====
* provided ``linkcode_resolve`` only supports Python domain
* generates https github links
* explicitly imports ``odoo``, so useless for anyone else
"""
def setup(app):
app.add_config_value('github_user', None, 'env')
app.add_config_value('github_project', None, 'env')
app.connect('html-page-context', add_doc_link)
def linkcode_resolve(domain, info):
""" Resolves provided object to corresponding github URL
"""
# TODO: js?
if domain != 'py':
return None
if not (app.config.github_user and app.config.github_project):
return None
module, fullname = info['module'], info['fullname']
# TODO: attributes/properties don't have modules, maybe try to look
# them up based on their cached host object?
if not module:
return None
obj = importlib.import_module(module)
for item in fullname.split('.'):
obj = getattr(obj, item, None)
if obj is None:
return None
# get original from decorated methods
try: obj = getattr(obj, '_orig')
except AttributeError: pass
try:
obj_source_path = inspect.getsourcefile(obj)
_, line = inspect.getsourcelines(obj)
except (TypeError, IOError):
# obj doesn't have a module, or something
return None
import odoo
# FIXME: make finding project root project-independent
project_root = os.path.join(os.path.dirname(odoo.__file__), '..')
return make_github_link(
app,
os.path.relpath(obj_source_path, project_root),
line)
app.config.linkcode_resolve = linkcode_resolve
def make_github_link(app, path, line=None, mode="blob"):
config = app.config
urlpath = "/{user}/{project}/{mode}/{branch}/{path}".format(
user=config.github_user,
project=config.github_project,
branch=config.version or 'master',
path=path,
mode=mode,
)
return urls.url_unparse((
'https',
'github.com',
urlpath,
'',
'' if line is None else 'L%d' % line
))
def add_doc_link(app, pagename, templatename, context, doctree):
""" Add github_link function linking to the current page on github """
if not app.config.github_user and app.config.github_project:
return
source_suffix = app.config.source_suffix
# in 1.3 source_suffix can be a list
# in 1.8 source_suffix can be a mapping
# FIXME: will break if we ever add support for !rst markdown documents maybe
if not isinstance(source_suffix, str):
source_suffix = next(iter(source_suffix))
# can't use functools.partial because 3rd positional is line not mode
context['github_link'] = lambda mode='edit': make_github_link(
app, 'doc/%s%s' % (pagename, source_suffix), mode=mode)
-129
View File
@@ -1,129 +0,0 @@
# -*- coding: utf-8 -*-
"""
Defines a "raw HTML" domain with a ``div[classes]`` and a number of roles
rendered more or less directly to HTML.
.. warning::
the purpose of this domain is *not* to document HTML or components
"""
from docutils import nodes, utils
from docutils.parsers.rst import Directive, directives
from docutils.parsers.rst.directives.body import LineBlock
import sphinx
import sphinx.roles
from sphinx.domains import Domain
def setup(app):
app.add_domain(HtmlDomain)
app.add_node(div, html=(
lambda self, node: self.body.append(self.starttag(node, 'div')),
lambda self, node: self.body.append('</div>\n')))
# override parameter was added for version sphinx >= 1.4, TypeError before
kw = {'override': True} if sphinx.version_info >= (1, 4) else {}
app.add_node(address, html=(
lambda self, node: self.body.append(self.starttag(node, 'address')),
lambda self, node: self.body.append('</address>\n')
), **kw) # docutils.nodes.address exists and is a bibliographic element
app.add_node(cite, html=(visit_cite, depart_cite))
for name, node in [('mark', mark), ('ins', insert), ('del', delete),
('s', strikethrough), ('u', underline), ('small', small),
('kbd', kbd), ('var', var), ('samp', samp)]:
addnode(app, node, name)
class div(nodes.General, nodes.Element): pass
class Div(Directive):
optional_arguments = 1
final_argument_whitespace = 1
has_content = True
def run(self):
self.assert_has_content()
text = '\n'.join(self.content)
try:
if self.arguments:
classes = directives.class_option(self.arguments[0])
else:
classes = []
except ValueError:
raise self.error(
'Invalid class attribute value for "%s" directive: "%s".'
% (self.name, self.arguments[0]))
node = div(text)
node['classes'].extend(classes)
self.add_name(node)
self.state.nested_parse(self.content, self.content_offset, node)
return [node]
class address(nodes.General, nodes.Element): pass
class Address(LineBlock):
def run(self):
[node] = super(Address, self).run()
ad = address(node.rawsource, *node.children)
return [ad]
class mark(nodes.Inline, nodes.TextElement): pass
class insert(nodes.Inline, nodes.TextElement): pass
class delete(nodes.Inline, nodes.TextElement): pass
class strikethrough(nodes.Inline, nodes.TextElement): pass
class underline(nodes.Inline, nodes.TextElement): pass
class small(nodes.Inline, nodes.TextElement): pass
class kbd(nodes.Inline, nodes.FixedTextElement): pass
class var(nodes.Inline, nodes.FixedTextElement): pass
class samp(nodes.Inline, nodes.FixedTextElement): pass
def makerole(node):
return lambda name, rawtext, text, lineno, inliner, options=None, content=None:\
([node(rawtext.strip(), text.strip())], [])
def addnode(app, node, nodename):
app.add_node(node, html=(
lambda self, n: self.body.append(self.starttag(n, nodename)),
lambda self, n: self.body.append('</%s>' % nodename)
))
def initialism(*args, **kwargs):
nodes, _ = sphinx.roles.abbr_role(*args, **kwargs)
[abbr] = nodes
abbr.attributes.setdefault('classes', []).append('initialism')
return [abbr], []
def cite_role(typ, rawtext, text, lineno, inliner, options=None, content=None):
text = utils.unescape(text)
m = sphinx.roles._abbr_re.search(text)
if m is None:
return [cite(text, text, **(options or {}))], []
content = text[:m.start()].strip()
source = m.group(1)
return [cite(content, content, source=source)], []
class cite(nodes.Inline, nodes.TextElement): pass
def visit_cite(self, node):
attrs = {}
if node.hasattr('source'):
attrs['title'] = node['source']
self.body.append(self.starttag(node, 'cite', '', **attrs))
def depart_cite(self, node):
self.body.append('</abbr>')
class HtmlDomain(Domain):
name = 'h'
label = 'HTML'
directives = {
'div': Div,
'address': Address,
}
roles = {
'mark': makerole(mark),
'ins': makerole(insert),
'del': makerole(delete),
's': makerole(strikethrough),
'u': makerole(underline),
'small': makerole(small),
'initialism': initialism,
'cite': cite_role,
'kbd': makerole(kbd),
'var': makerole(var),
'samp': makerole(samp),
}
-148
View File
@@ -1,148 +0,0 @@
# -*- coding: utf-8 -*-
from . import pygments_override
from . import switcher
from . import translator
import sphinx.environment
try:
from sphinx.environment.adapters import toctree
except ImportError:
toctree = None
import sphinx.builders.html
from docutils import nodes
def setup(app):
if hasattr(app, 'set_translator'):
app.set_translator('html', translator.BootstrapTranslator)
else:
if getattr(app.config, 'html_translator_class', None):
app.warn("Overriding the explicitly set html_translator_class setting",
location="odoo extension")
app.config.html_translator_class = 'odoo_ext.translator.BootstrapTranslator'
add_js_file = getattr(app, 'add_js_file', None) or app.add_javascript
for f in ['jquery.min.js', 'bootstrap.js', 'doc.js', 'jquery.noconflict.js']:
add_js_file(f)
switcher.setup(app)
app.add_config_value('odoo_cover_default', None, 'env')
app.add_config_value('odoo_cover_external', {}, 'env')
app.add_config_value('odoo_cover_default_external', lambda conf: conf.odoo_cover_default, 'env')
app.connect('html-page-context', update_meta)
def update_meta(app, pagename, templatename, context, doctree):
meta = context.get('meta')
if meta is None:
meta = context['meta'] = {}
meta.setdefault('banner', app.config.odoo_cover_default)
def navbarify(node, navbar=None):
"""
:param node: toctree node to navbarify
:param navbar: Whether this toctree is a 'main' navbar, a 'side' navbar or
not a navbar at all
"""
if navbar == 'main':
# add classes to just toplevel
node['classes'].extend(['nav', 'navbar-nav', 'navbar-right'])
for list_item in node.children:
# bullet_list
# list_item
# compact_paragraph
# reference
# bullet_list
# list_item
# compact_paragraph
# reference
# no bullet_list.list_item -> don't dropdownify
if len(list_item.children) < 2 or not list_item.children[1].children:
continue
list_item['classes'].append('dropdown')
# list_item.compact_paragraph.reference
link = list_item.children[0].children[0]
link['classes'].append('dropdown-toggle')
link.attributes['data-toggle'] = 'dropdown'
# list_item.bullet_list
list_item.children[1]['classes'].append('dropdown-menu')
elif navbar is None:
for n in node.traverse(nodes.reference):
# list_item
# compact_paragraph
# reference <- starting point
# bullet_list
# list_item+
# if the current list item (GP of current node) has bullet list
# children, unref it
list_item = n.parent.parent
# only has a reference -> ignore
if len(list_item.children) < 2:
continue
# no subrefs -> ignore
if not list_item.children[1].children:
continue
# otherwise replace reference node by an inline (so it can still be styled)
para = n.parent
para.remove(n)
para.append(nodes.inline('', '', *n.children))
def resolve_content_toctree(
environment, docname, builder, toctree, prune=True, maxdepth=0,
titles_only=False, collapse=False, includehidden=False):
"""Alternative toctree resolution for main content: don't resolve the
toctree, just handle it as a normal node, in the translator
"""
return toctree
class monkey(object):
def __init__(self, obj):
self.obj = obj
def __call__(self, fn):
name = fn.__name__
old = getattr(self.obj, name)
setattr(self.obj, name, lambda self_, *args, **kwargs: \
fn(old, self_, *args, **kwargs))
if toctree:
# 1.6 and above use a new toctree adapter object for processing rather
# than functions on the BuildEnv & al
@monkey(toctree.TocTree)
def resolve(old_resolve, tree, docname, *args, **kwargs):
if docname == tree.env.config.master_doc:
return resolve_content_toctree(tree.env, docname, *args, **kwargs)
return old_resolve(tree, docname, *args, **kwargs)
@monkey(sphinx.environment.BuildEnvironment)
def resolve_toctree(old_resolve, self, docname, *args, **kwargs):
""" If navbar, bootstrapify TOC to yield a navbar
"""
navbar = kwargs.pop('navbar', None)
if docname == self.config.master_doc and not navbar:
return resolve_content_toctree(self, docname, *args, **kwargs)
toc = old_resolve(self, docname, *args, **kwargs)
if toc is None:
return None
navbarify(toc[0], navbar=navbar)
return toc
@monkey(sphinx.builders.html.StandaloneHTMLBuilder)
def render_partial(old_partial, self, node):
if isinstance(node, nodes.bullet_list) and node.children:
# side nav?
# remove single top-level item
# bullet_list/0(list_item)/1(bullet_list)
level1 = node.children[0].children
if len(level1) > 1:
node = level1[1]
node['classes'].extend(['list-group', 'nav', 'text-left'])
for n in node.traverse():
if isinstance(n, nodes.list_item):
n['classes'].append('list-group-item')
elif isinstance(n, nodes.reference):
n['classes'].append('ripple')
else:
node.clear()
return old_partial(self, node)
@@ -1,23 +0,0 @@
{# warning: if doc structure change, these rules may have to change as well #}
{# ===== VARIABLES ====== #}
{% set master_doc_short_name = 'Developer Doc' %}
{% if pagename == master_doc %}
<li><a href="{{ pathto(master_doc) }}" class="active">{{ master_doc_short_name }}</a></li>
{% else %}
{% for parent in parents %}
{% if loop.length > 1%}
{% if loop.first %}
<li><a href="{{ pathto(master_doc) }}">{{ master_doc_short_name }}</a></li>
{% else %}
{% if loop.index == 2 %}
<li><a href="{{ parent.link|e }}">{{parent.title}}</a></li>
{% endif %}
{% endif %}
{% else %}
<li><a href="{{ pathto(master_doc) }}">{{ master_doc_short_name }}</a></li>
{% endif %}
{% endfor %}
<li class="active"><a href="#">{{ meta.get('main-title', title) }}</a></li>
{% endif %}
-339
View File
@@ -1,339 +0,0 @@
{% extends "basic/layout.html" %}
{% set classes = [] %}
{% if pagename == master_doc %}
{% set classes = classes + ['index'] %}
{% endif %}
{% if 'code-column' in meta %}
{% set classes = classes + ['has_code_col'] %}
{% endif %}
{%- block doctype -%}
<!doctype html>
{%- endblock -%}
{%- block linktags %}
<link href="https://fonts.googleapis.com/css?family=Work+Sans:500,600" rel="stylesheet">
<link rel="canonical" href="{{ canonical }}" />
{{- super() }}
{%- endblock %}
{%- block sidebar1 -%}{%- endblock -%}
{%- block sidebar2 -%}{%- endblock -%}
{%- block relbar1 -%}{%- endblock -%}
{%- block relbar2 -%}{%- endblock -%}
{%- block footer -%}
{%- if google_analytics_key -%}
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', '{{ google_analytics_key }}', 'auto');
ga('send','pageview');
</script>
{%- endif -%}
{%- endblock -%}
{%- block header -%}
<header class="o_main_header o_has_sub_nav o_inverted {{ ' '.join(classes) }}">
<div class="o_main_header_main">
<a class="pull-left o_logo" href="/"></a>
<a href="#" class="o_mobile_menu_toggle visible-xs-block pull-right">
<span class="sr-only">Toggle navigation</span>
<span class="mdi-navigation-menu"></span>
</a>
<div class="o_header_buttons">
<a href="http://www.odoo.com/trial" class="btn btn-primary">Start Now</a>
</div>
<ul class="o_primary_nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle">Apps</a>
<div class="dropdown-menu o_secondary_nav">
<div class="container">
<div class="row">
<div class="col-sm-3 o_website_apps">
<div class="o_nav_app_family">
<span></span> Websites
<div>Build great user experience</div>
</div>
<ul>
<li><a href="https://www.odoo.com/page/website-builder">Website Builder</a></li>
<li><a href="https://www.odoo.com/page/e-commerce">eCommerce</a></li>
<li><a href="https://www.odoo.com/page/blog-engine">Blogs</a></li>
<li><a href="https://www.odoo.com/page/community-builder">Forums</a></li>
<li><a href="https://www.odoo.com/page/slides">Slides</a></li>
<li><a href="https://adspike.odoo.com">SEA</a></li>
</ul>
</div>
<div class="col-sm-3 o_sale_apps">
<div class="o_nav_app_family">
<span></span> Sales
<div>Boost your success rate</div>
</div>
<ul>
<li><a href="https://www.odoo.com/page/sales">Sales</a></li>
<li><a href="https://www.odoo.com/page/crm">CRM</a></li>
<li><a href="https://www.odoo.com/page/billing">Invoicing</a></li>
<li class="dropdown">
<a href="#0" class="dropdown-toggle">Point of Sale</a>
<ul>
<li><a href="https://www.odoo.com/page/point-of-sale">Shops</a></li>
<li><a href="https://www.odoo.com/page/pos-restaurant">Restaurants</a></li>
</ul>
</li>
<li><a href="https://www.odoo.com/page/subscriptions">Subscriptions</a></li>
<li><a href="https://www.odoo.com/page/sign">Sign</a></li>
</ul>
</div>
<div class="col-sm-3 o_operation_apps">
<div class="o_nav_app_family">
<span></span> Operations
<div>It's all about efficiency</div>
</div>
<ul>
<li><a href="https://www.odoo.com/page/accounting">Accounting</a></li>
<li><a href="https://www.odoo.com/page/project-management">Project</a></li>
<li class="dropdown">
<a href="#0" class="dropdown-toggle">Human Resources</a>
<ul>
<li><a href="https://www.odoo.com/page/recruitment">Recruitment</a></li>
<li><a href="https://www.odoo.com/page/employees">Employees</a></li>
<li><a href="https://www.odoo.com/page/expenses">Expenses</a></li>
<li><a href="https://www.odoo.com/page/appraisal">Appraisal</a></li>
<li><a href="https://www.odoo.com/page/fleet">Fleet</a></li>
<li><a href="https://www.odoo.com/page/leaves">Leaves</a></li>
</ul>
</li>
<li><a href="https://www.odoo.com/page/warehouse">Inventory</a></li>
<li><a href="https://www.odoo.com/page/purchase">Purchase</a></li>
<li class="dropdown">
<a href="#0" class="dropdown-toggle">Manufacturing</a>
<ul>
<li><a href="https://www.odoo.com/page/manufacturing">MRP</a></li>
<li><a href="https://www.odoo.com/page/plm">PLM</a></li>
<li><a href="https://www.odoo.com/page/maintenance">Maintenance</a></li>
<li><a href="https://www.odoo.com/page/quality">Quality</a></li>
</ul>
</li>
</ul>
</div>
<div class="col-sm-3 o_productivity_apps">
<div class="o_nav_app_family">
<span></span> Productivity Tools
<div>Great Tools = Happy People</div>
</div>
<ul>
<li class="dropdown">
<a href="#0" class="dropdown-toggle">Communication</a>
<ul>
<li><a href="https://www.odoo.com/page/discuss">Discuss</a></li>
<li><a href="https://www.odoo.com/page/discuss-groups">Mailing Lists</a></li>
<li><a href="https://www.odoo.com/page/notes">Notes</a></li>
<li><a href="#">Help desk</a></li>
<li><a href="#">Appointment</a></li>
</ul>
</li>
<li><a href="https://www.odoo.com/page/timesheet">Timesheet</a></li>
<li><a href="https://www.odoo.com/page/email-marketing">Email Marketing</a></li>
<li><a href="https://www.odoo.com/page/events">Events</a></li>
<li><a href="https://www.odoo.com/page/survey">Survey</a></li>
<li><a href="https://www.odoo.com/page/live-chat">Live Chat</a></li>
</ul>
</div>
</div>
</div>
<a href="http://www.odoo.com/apps/modules" class="o_store_link"><i class="fa fa-cube fa-fw"></i> Third party apps</a>
</div>
</li>
<li><a href="https://www.odoo.com/page/tour">Tour</a></li>
<li><a href="https://www.odoo.com/pricing">Pricing</a></li>
<li><a href="https://www.odoo.com/page/docs">Docs</a></li>
</ul>
</div>
<nav class="navbar o_sub_nav">
<div class="container">
<div class="navbar-header visible-xs">
<button type="button" class="navbar-toggle collapsed text-left btn-block" data-toggle="collapse" data-target="#o_sub-menu" aria-expanded="false">
Navigate
<span class="mdi-hardware-keyboard-arrow-down pull-right"></span>
</button>
</div>
<div class="collapse navbar-collapse" id="o_sub-menu">
<ol class="o_breadcrumb breadcrumb nav navbar-left">
{% block breadcrumb_desktop %}
{% include "breadcrumb_list.html" %}
{% endblock %}
</ol>
<div class="call-to-action navbar-right hidden-xs">
<a href="http://www.odoo.com/trial" class="btn btn-primary">Start Now</a>
</div>
<ul class="navbar-nav navbar-right nav o_sub_nav_actions">
{% if pagename != master_doc %}
<li class="divider"></li>
{% endif%}
{% block switchers_desktop %}
{% include "switchers_list.html" %}
{% endblock %}
</ul>
<ul class="nav navbar-nav navbar-right">
{% if languages or versions %}
<li class="divider"></li>
{% endif %}
{% block sub_menu_desktop %}
{% include "sub-menu_list.html" %}
{% endblock %}
{% if languages or versions %}
<li class="divider"></li>
{% endif %}
</ul>
</div>
</div>
</nav>
</header>
{%- endblock -%}
{%- block content -%}
<div id="wrap" class="{{' '.join(classes) }}">
{% if meta['banner'] %}
{% set has_banner = 'has_banner' %}
{% endif %}
<figure class="card top {{ has_banner }}">
<span class="card-img" {% if meta['banner'] %}style="background-image: url('{{ pathto('_static/' + meta['banner'], True) }}');"{% endif %}></span>
<div class="container text-center">
<h1> {{ meta.get('main-title', title) }} </h1>
</div>
</figure>
{% if 'code-column' in meta %}
{% set container = 'container-fluid' %}
{% else %}
{% set container = 'container' %}
{% endif %}
<main class="container {{ ' '.join(classes) }}">
{% if pagename != master_doc %}
<div class="o_content row">
{% if 'has-toc' not in meta and not (pagename in toc) %}
<aside>
<div class="navbar-aside text-center">
{{ toc }}
{% if github_link %}
<p class="gith-container"><a href="{{ github_link(mode='edit') }}" class="gith-link">
Edit on GitHub
</a></p>
{% endif %}
</div>
</aside>
{% endif %}
<article class="doc-body {% if 'has-toc' in meta %}doc-toc{% endif %}{% if pagename in toc%}index-category{% endif %}">
{% endif %}
{% block body %} {% endblock %}
{% if pagename != master_doc %}</article>
</div>
{% endif %}
<div id="mask"></div>
</main>
</div>
<div class="floating_action_container">
<a id="floating_action" class="ripple" href="#">
<i class="mdi-action-explore"></i>
</a>
<div id="floating_action_menu">
<span class="bubble"></span>
<ul class="list-group content">
<li class="list-group-item ripple"><a>Cras justo odio</a></li>
<li class="list-group-item ripple"><a>Dapibus ac facilisis in</a></li>
<li class="list-group-item ripple"><a>Morbi leo risus</a></li>
<li class="list-group-item ripple"><a>Porta ac consectetur ac</a></li>
<li class="list-group-item ripple"><a>Vestibulum at eros</a></li>
</ul>
</div>
</div>
<footer>
<div id="footer" class="container">
<span class="o_logo o_logo_inverse center-block o_footer_logo"></span>
<div class="row">
<div class="col-sm-7 col-md-7 col-lg-6">
<div class="row">
<div class="col-xs-6 col-sm-4">
<span class="menu_title">Community</span>
<ul>
<li><a href="https://github.com/odoo/odoo">Github</a></li>
<li><a href="http://www.odoo.com/page/download">Download</a></li>
<li class="divider"></li>
<li><a href="https://runbot.odoo.com">Runbot</a></li>
<li><a href="https://github.com/odoo/odoo/wiki/Translations">Translations</a></li>
<li class="divider"></li>
<li><a href="http://www.odoo.com/page/odoo-community">Mailing Lists</a></li>
<li><a href="http://www.odoo.com/forum/help-1">Forum</a></li>
</ul>
</div>
<div class="col-xs-6 col-sm-4">
<span class="menu_title">Services</span>
<ul>
<li><a href="https://www.odoo.sh">Odoo Cloud Platform</a></li>
<li class="divider"></li>
<li><a href="http://www.odoo.com/help">Support</a></li>
<li><a href="https://upgrade.odoo.com">Upgrade</a></li>
<li class="divider"></li>
<li><a href="http://www.odoo.com/partners">Find a partner</a></li>
<li><a href="http://www.odoo.com/page/become-a-partner">Become a partner</a></li>
<li class="divider"></li>
<li><a href="http://training.odoo.com/courses/odoo-functional">Training Center</a></li>
<li><a href="http://www.odoo.com/page/education-program">Education</a></li>
<li class="divider"></li>
<li><a href="http://www.odoo.com/page/security">Security</a></li>
</ul>
</div>
<div class="col-xs-12 col-sm-4 mb64">
<span class="menu_title">About us</span>
<ul>
<li><a href="http://www.odoo.com/page/about-us">Our company</a></li>
<li><a href="http://www.odoo.com/page/contactus">Contact</a></li>
<li class="divider" />
<li><a href="http://www.odoo.com/event">Events</a></li>
<li><a href="http://www.odoo.com/blog">Blog</a></li>
<li><a href="http://www.odoo.com/blog/6">Customers</a></li>
<li class="divider" />
<li><a href="http://www.odoo.com/jobs">Jobs</a></li>
<li class="divider" />
<li><a href="http://www.odoo.com/page/legal">Legal</a> | <a href="http://www.odoo.com/privacy">Privacy</a></li>
</ul>
</div>
</div>
</div>
<div class="col-sm-5 col-md-4 col-md-offset-1 col-lg-5 col-lg-offset-1">
<p>
<small>
Odoo is a suite of open source business apps that cover all your company needs: CRM, eCommerce, accounting, inventory, point of sale, project management, etc.
<br/><br/>
Odoo's unique value proposition is to be at the same time very easy to use and fully integrated.
</small>
</p>
</div>
</div>
</div>
<div class="o_footer_bottom">
<div class="container">
<a class="small" href="http://www.odoo.com/page/website-builder">Website made with <span class="o_logo o_logo_inverse o_logo_15"></span></a>
<div class="social-links pull-right">
<a href="http://www.odoo.com/web/about/facebook"><i class="fa fa-facebook"></i></a>
<a href="http://www.odoo.com/web/about/twitter"><i class="fa fa-twitter"></i></a>
<a href="http://www.odoo.com/web/about/linkedin"><i class="fa fa-linkedin"></i></a>
<a href="mailto:info@odoo.com"><i class="fa fa-envelope"></i></a>
</div>
</div>
</div>
</footer>
{%- endblock -%}
@@ -1,126 +0,0 @@
# -*- coding: utf-8 -*-
from pygments.style import Style
from pygments.token import Keyword, Name, Comment, String, Error, Text, \
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
BACKGROUND = '#272727'
CURRENT_LINE = "#41323f"
SELECTION = "#4f424c"
FOREGROUND = "#e7e9db"
COMMENT = "#776e71"
RED = "#ef6155"
ORANGE = "#f99b15"
YELLOW = "#fec418"
GREEN = "#48b685"
AQUA = "#5bc4bf"
BLUE = "#06b6ef"
PURPLE = "#815ba4"
class OdooStyle(Style):
"""Modified version of Paraiso Dark style to avoid Pygments version
annoyances.
Paraíso (Dark) by Jan T. Sott
Pygments template by Jan T. Sott (https://github.com/idleberg)
Created with Base16 Builder by Chris Kempson
(https://github.com/chriskempson/base16-builder).
:copyright: Copyright 2006-2014 by the Pygments team
:license: BSD
"""
default_style = ''
background_color = BACKGROUND
highlight_color = SELECTION
styles = {
# No corresponding class for the following:
Text: FOREGROUND,
Whitespace: "",
Error: RED,
Other: "",
Comment: COMMENT,
Comment.Multiline: "",
Comment.Preproc: "",
Comment.Single: "",
Comment.Special: "",
Keyword: '#cb49a8',
Keyword.Constant: "",
Keyword.Declaration: "",
Keyword.Namespace: AQUA,
Keyword.Pseudo: "",
Keyword.Reserved: "",
Keyword.Type: YELLOW,
Operator: AQUA,
Operator.Word: "",
Punctuation: FOREGROUND,
Name: FOREGROUND,
Name.Attribute: BLUE,
Name.Builtin: "",
Name.Builtin.Pseudo: "",
Name.Class: YELLOW,
Name.Constant: RED,
Name.Decorator: AQUA,
Name.Entity: "",
Name.Exception: RED,
Name.Function: BLUE,
Name.Property: "",
Name.Label: "",
Name.Namespace: YELLOW,
Name.Other: BLUE,
Name.Tag: '#21b799',
Name.Variable: RED,
Name.Variable.Class: "",
Name.Variable.Global: "",
Name.Variable.Instance: "",
Number: ORANGE,
Number.Float: "",
Number.Hex: "",
Number.Integer: "",
Number.Integer.Long: "",
Number.Oct: "",
Literal: ORANGE,
Literal.Date: GREEN,
String: GREEN,
String.Backtick: "",
String.Char: FOREGROUND,
String.Doc: COMMENT,
String.Double: "",
String.Escape: ORANGE,
String.Heredoc: "",
String.Interpol: ORANGE,
String.Other: "",
String.Regex: "",
String.Single: "",
String.Symbol: "",
Generic: "",
Generic.Deleted: RED,
Generic.Emph: "italic",
Generic.Error: "",
Generic.Heading: "bold " + FOREGROUND,
Generic.Inserted: GREEN,
Generic.Output: "",
Generic.Prompt: "bold " + COMMENT,
Generic.Strong: "bold",
Generic.Subheading: "bold " + AQUA,
Generic.Traceback: "",
}
import types
import sys
modname = 'pygments.styles.odoo'
m = types.ModuleType(modname)
m.OdooStyle = OdooStyle
sys.modules[modname] = m
-339
View File
@@ -1,339 +0,0 @@
// ====== Aside =======
// ======================
aside {
.pseudo-col();
width: 100%;
max-width: 240px;
position: static;
padding: 0;
display: block;
border-right: 1px solid @gray-lighter;
@media (max-width:@screen-md-min) {
display: none;
}
}
.navbar-aside {
position: relative;
overflow: hidden;
font-family: @headings-font-family;
font-size: 0.85em;
line-height: 1.5;
overflow-y: auto;
&.affix {
z-index: 2;
top: @w-sub-nav-height - 1;
position: fixed;
backface-visibility: hidden;
@media (max-width:@screen-md-min) {
display: none;
}
}
> ul.list-group {
margin: 0;
z-index: 0;
}
> h3 {
margin: 0;
padding: 15px 0 10px;
text-transform: uppercase;
font-weight: @fw_semibold;
font-size: 16px;
color: @text-color;
overflow-x: hidden;
position: relative;
z-index: 1;
.box-shadow(0 10px 9px -10px #d2d2d2);
}
.gith-container {
margin: 0;
z-index: 1;
position: relative;
overflow-x: hidden;
.gith-link {
color: @text-color;
text-decoration: none;
display: inline-block;
position: relative;
margin: 10px 0;
font-family: @font-family-base;
&:hover {
color: @headings-color;
text-decoration: none;
}
&:before {
.square(1em);
content: '';
position: relative;
background-image: url('img/github-square_32.png');
background-size: 100%;
display: inline-block;
vertical-align: middle;
.translate(0;-1px);
}
}
}
//-- All menu levels
ul {
padding: 0;
position: relative;
margin-bottom: 0 !important;
li {
padding: 0;
margin-bottom: 0;
&:first-child, &:last-child {
// Reset border radius
border-radius: 0;
}
&:first-child {
overflow: hidden;
border-radius: 2px 0 0 0;
}
> a {
display: block;
padding: 5px 15px;
color: @text-color;
line-height: 1.2;
&:focus, &:hover {
color: @headings-color;
}
}
&.parent > a:after{
content: '\e7c1';
font-family: 'Material-Design-Icons';
opacity: 0.8;
margin-top: -6px;
.o-position-absolute(50%, 10px);
.rotate(90deg);
.transition(transform 0.2s);
}
&.parent.active > a:after{
.rotate(0);
}
}
//-- Inner menu list
ul {
max-height: 0;
background-color: fade(@gray-light, 20%);
padding: 0px;
overflow: hidden;
margin-left: 0;
box-shadow: inset 0 3px 5px -2px @gray-light;
.transition(max-height 0.3s @o-ease);
> li:not(:last-child) {
box-shadow: 0 1px 0 fade(black, 4%);
&.active > ul > li:not(:first-child) {
border-top: 1px solid @gray-lighter;
}
}
}
}
//-- Active lis
li.active {
&, &:focus, &:hover {
border-color: @gray-lighter;
background: transparent;
> a {
font-weight: bold;
color: @headings-color;
}
}
> ul {
max-height: 1000px;
}
}
// First menu level
> ul > li {
background: white;
border-bottom: 1px solid fade(black, 8%);
> a {
padding: 10px 15px;
border-left: 3px solid transparent;
}
&:focus, &:hover {
> a {
border-left: 3px solid @gray-light;
background-color: transparent;
}
}
&.active, &.active:focus, &.active:hover {
> a {
border-left: 3px solid @brand-primary;
}
}
&.active.parent + li {
box-shadow: 0 -3px 5px -4px fade(@gray-darker, 40%);
}
> ul > li {
> a {
padding-left: 20px;
}
> ul > li {
> a {
padding-left: 30px;
}
> ul > li > a {
padding-left: 40px;
}
}
}
}
}
.floating_action_container {
position: fixed;
right: 8px;
bottom: 8px;
width: auto;
z-index: @zIndex--float_action;
@media (min-width:@screen-md-min) {
display: none;
}
}
#floating_action {
.square(56px);
display: inline-block;
z-index: 0;
background-color: transparent;
border-radius: 50%;
padding: 16px;
box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.1), 0 6px 20px 0 rgba(0, 0, 0, 0.05);
background-color: @brand-primary;
.transition(transform 500ms @o-ease);
i {
color: white;
}
&.active {
.transform(~ "translateZ(0px) rotateZ(-180deg)");
}
}
#floating_action_menu {
.o-position-absolute(auto,0,0);
z-index: 1;
overflow: hidden;
width: 380px;
padding: 0;
display: block;
border-radius: 2px;
visibility: hidden;
.transition(all 0.5s);
.box-shadow(0 0 0 transparent);
.content {
margin: 0;
opacity:0;
li {
border: none;
border-top: 1px solid #DDD;
&:first-child {
border-top: none;
}
}
a {
display: inline-block;
cursor: pointer;
color: @headings-color;
text-decoration: none;
float: left;
width: 100%;
padding: 5px 10px;
}
}
.bubble {
.o-position-absolute(auto,28px,28px);
.square(1px);
background: @brand-primary;
content: '';
color: #fff;
border-radius: 50%;
.transition(all 300ms @o-ease);
}
&:before {
.bubble;
background: white;
}
&.active {
display: block;
visibility: visible;
.transition(all 500ms @o-ease);
&:before, .bubble {
.size(1000px; 2000px);
border-radius: 50%;
opacity: 0;
margin-right: -500px;
margin-bottom: -500px;
display: block;
.transition(all .4s @o-ease);
}
&:before {
.opacity(1);
}
.content {
position: relative;
z-index: 1;
.opacity(1);
.transition(all 500ms @ease-material-3);
}
}
}
#mask {
.opacity(0);
position: fixed;
z-index: @zIndex--mask;
top: 0;
left: 0;
.size(100%; 0);
background-color: fade(black, 20%);
.transition(opacity .3s);
&.active {
.opacity(1);
display: block;
.square(100%);
.transition(opacity .3s);
}
}
@@ -1,73 +0,0 @@
//
// Alerts
// --------------------------------------------------
// Base styles
// -------------------------
.alert {
padding: @alert-padding;
margin-bottom: @line-height-computed;
border: 1px solid transparent;
border-radius: @alert-border-radius;
// Headings for larger alerts
h4 {
margin-top: 0;
// Specified for the h4 to prevent conflicts of changing @headings-color
color: inherit;
}
// Provide class for links that match alerts
.alert-link {
font-weight: @alert-link-font-weight;
}
// Improve alignment and spacing of inner content
> p,
> ul {
margin-bottom: 0;
}
> p + p {
margin-top: 5px;
}
}
// Dismissible alerts
//
// Expand the right padding and account for the close button's positioning.
.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0.
.alert-dismissible {
padding-right: (@alert-padding + 20);
// Adjust close link position
.close {
position: relative;
top: -2px;
right: -21px;
color: inherit;
}
}
// Alternate styles
//
// Generate contextual modifier classes for colorizing the alert.
.alert-success {
.alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);
}
.alert-info {
.alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);
}
.alert-warning {
.alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);
}
.alert-danger {
.alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);
}
@@ -1,66 +0,0 @@
//
// Badges
// --------------------------------------------------
// Base class
.badge {
display: inline-block;
min-width: 10px;
padding: 3px 7px;
font-size: @font-size-small;
font-weight: @badge-font-weight;
color: @badge-color;
line-height: @badge-line-height;
vertical-align: middle;
white-space: nowrap;
text-align: center;
background-color: @badge-bg;
border-radius: @badge-border-radius;
// Empty badges collapse automatically (not available in IE8)
&:empty {
display: none;
}
// Quick fix for badges in buttons
.btn & {
position: relative;
top: -1px;
}
.btn-xs &,
.btn-group-xs > .btn & {
top: 0;
padding: 1px 5px;
}
// Hover state, but only for links
a& {
&:hover,
&:focus {
color: @badge-link-hover-color;
text-decoration: none;
cursor: pointer;
}
}
// Account for badges in navs
.list-group-item.active > &,
.nav-pills > .active > a > & {
color: @badge-active-color;
background-color: @badge-active-bg;
}
.list-group-item > & {
float: right;
}
.list-group-item > & + & {
margin-right: 5px;
}
.nav-pills > li > a > & {
margin-left: 3px;
}
}
@@ -1,56 +0,0 @@
/*!
* Bootstrap v3.3.6 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
// Core variables and mixins
@import "variables.less";
@import "mixins.less";
// Reset and dependencies
@import "normalize.less";
@import "print.less";
@import "glyphicons.less";
// Core CSS
@import "scaffolding.less";
@import "type.less";
@import "code.less";
@import "grid.less";
@import "tables.less";
@import "forms.less";
@import "buttons.less";
// Components
@import "component-animations.less";
@import "dropdowns.less";
@import "button-groups.less";
@import "input-groups.less";
@import "navs.less";
@import "navbar.less";
@import "breadcrumbs.less";
@import "pagination.less";
@import "pager.less";
@import "labels.less";
@import "badges.less";
@import "jumbotron.less";
@import "thumbnails.less";
@import "alerts.less";
@import "progress-bars.less";
@import "media.less";
@import "list-group.less";
@import "panels.less";
@import "responsive-embed.less";
@import "wells.less";
@import "close.less";
// Components w/ JavaScript
@import "modals.less";
@import "tooltip.less";
@import "popovers.less";
@import "carousel.less";
// Utility classes
@import "utilities.less";
@import "responsive-utilities.less";
@@ -1,26 +0,0 @@
//
// Breadcrumbs
// --------------------------------------------------
.breadcrumb {
padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;
margin-bottom: @line-height-computed;
list-style: none;
background-color: @breadcrumb-bg;
border-radius: @border-radius-base;
> li {
display: inline-block;
+ li:before {
content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space
padding: 0 5px;
color: @breadcrumb-color;
}
}
> .active {
color: @breadcrumb-active-color;
}
}
@@ -1,244 +0,0 @@
//
// Button groups
// --------------------------------------------------
// Make the div behave like a button
.btn-group,
.btn-group-vertical {
position: relative;
display: inline-block;
vertical-align: middle; // match .btn alignment given font-size hack above
> .btn {
position: relative;
float: left;
// Bring the "active" button to the front
&:hover,
&:focus,
&:active,
&.active {
z-index: 2;
}
}
}
// Prevent double borders when buttons are next to each other
.btn-group {
.btn + .btn,
.btn + .btn-group,
.btn-group + .btn,
.btn-group + .btn-group {
margin-left: -1px;
}
}
// Optional: Group multiple button groups together for a toolbar
.btn-toolbar {
margin-left: -5px; // Offset the first child's margin
&:extend(.clearfix all);
.btn,
.btn-group,
.input-group {
float: left;
}
> .btn,
> .btn-group,
> .input-group {
margin-left: 5px;
}
}
.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
border-radius: 0;
}
// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match
.btn-group > .btn:first-child {
margin-left: 0;
&:not(:last-child):not(.dropdown-toggle) {
.border-right-radius(0);
}
}
// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it
.btn-group > .btn:last-child:not(:first-child),
.btn-group > .dropdown-toggle:not(:first-child) {
.border-left-radius(0);
}
// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)
.btn-group > .btn-group {
float: left;
}
.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
border-radius: 0;
}
.btn-group > .btn-group:first-child:not(:last-child) {
> .btn:last-child,
> .dropdown-toggle {
.border-right-radius(0);
}
}
.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {
.border-left-radius(0);
}
// On active and open, don't show outline
.btn-group .dropdown-toggle:active,
.btn-group.open .dropdown-toggle {
outline: 0;
}
// Sizing
//
// Remix the default button sizing classes into new ones for easier manipulation.
.btn-group-xs > .btn { &:extend(.btn-xs); }
.btn-group-sm > .btn { &:extend(.btn-sm); }
.btn-group-lg > .btn { &:extend(.btn-lg); }
// Split button dropdowns
// ----------------------
// Give the line between buttons some depth
.btn-group > .btn + .dropdown-toggle {
padding-left: 8px;
padding-right: 8px;
}
.btn-group > .btn-lg + .dropdown-toggle {
padding-left: 12px;
padding-right: 12px;
}
// The clickable button for toggling the menu
// Remove the gradient and set the same inset shadow as the :active state
.btn-group.open .dropdown-toggle {
.box-shadow(inset 0 3px 5px rgba(0,0,0,.125));
// Show no shadow for `.btn-link` since it has no other button styles.
&.btn-link {
.box-shadow(none);
}
}
// Reposition the caret
.btn .caret {
margin-left: 0;
}
// Carets in other button sizes
.btn-lg .caret {
border-width: @caret-width-large @caret-width-large 0;
border-bottom-width: 0;
}
// Upside down carets for .dropup
.dropup .btn-lg .caret {
border-width: 0 @caret-width-large @caret-width-large;
}
// Vertical button groups
// ----------------------
.btn-group-vertical {
> .btn,
> .btn-group,
> .btn-group > .btn {
display: block;
float: none;
width: 100%;
max-width: 100%;
}
// Clear floats so dropdown menus can be properly placed
> .btn-group {
&:extend(.clearfix all);
> .btn {
float: none;
}
}
> .btn + .btn,
> .btn + .btn-group,
> .btn-group + .btn,
> .btn-group + .btn-group {
margin-top: -1px;
margin-left: 0;
}
}
.btn-group-vertical > .btn {
&:not(:first-child):not(:last-child) {
border-radius: 0;
}
&:first-child:not(:last-child) {
.border-top-radius(@btn-border-radius-base);
.border-bottom-radius(0);
}
&:last-child:not(:first-child) {
.border-top-radius(0);
.border-bottom-radius(@btn-border-radius-base);
}
}
.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
border-radius: 0;
}
.btn-group-vertical > .btn-group:first-child:not(:last-child) {
> .btn:last-child,
> .dropdown-toggle {
.border-bottom-radius(0);
}
}
.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {
.border-top-radius(0);
}
// Justified button groups
// ----------------------
.btn-group-justified {
display: table;
width: 100%;
table-layout: fixed;
border-collapse: separate;
> .btn,
> .btn-group {
float: none;
display: table-cell;
width: 1%;
}
> .btn-group .btn {
width: 100%;
}
> .btn-group .dropdown-menu {
left: auto;
}
}
// Checkbox and radio options
//
// In order to support the browser's form validation feedback, powered by the
// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use
// `display: none;` or `visibility: hidden;` as that also hides the popover.
// Simply visually hiding the inputs via `opacity` would leave them clickable in
// certain cases which is prevented by using `clip` and `pointer-events`.
// This way, we ensure a DOM element is visible to position the popover from.
//
// See https://github.com/twbs/bootstrap/pull/12794 and
// https://github.com/twbs/bootstrap/pull/14559 for more information.
[data-toggle="buttons"] {
> .btn,
> .btn-group > .btn {
input[type="radio"],
input[type="checkbox"] {
position: absolute;
clip: rect(0,0,0,0);
pointer-events: none;
}
}
}
@@ -1,166 +0,0 @@
//
// Buttons
// --------------------------------------------------
// Base styles
// --------------------------------------------------
.btn {
display: inline-block;
margin-bottom: 0; // For input.btn
font-weight: @btn-font-weight;
text-align: center;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214
border: 1px solid transparent;
white-space: nowrap;
.button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @btn-border-radius-base);
.user-select(none);
&,
&:active,
&.active {
&:focus,
&.focus {
.tab-focus();
}
}
&:hover,
&:focus,
&.focus {
color: @btn-default-color;
text-decoration: none;
}
&:active,
&.active {
outline: 0;
background-image: none;
.box-shadow(inset 0 3px 5px rgba(0,0,0,.125));
}
&.disabled,
&[disabled],
fieldset[disabled] & {
cursor: @cursor-disabled;
.opacity(.65);
.box-shadow(none);
}
a& {
&.disabled,
fieldset[disabled] & {
pointer-events: none; // Future-proof disabling of clicks on `<a>` elements
}
}
}
// Alternate buttons
// --------------------------------------------------
.btn-default {
.button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);
}
.btn-primary {
.button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);
}
// Success appears as green
.btn-success {
.button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);
}
// Info appears as blue-green
.btn-info {
.button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);
}
// Warning appears as orange
.btn-warning {
.button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);
}
// Danger and error appear as red
.btn-danger {
.button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);
}
// Link buttons
// -------------------------
// Make a button look and behave like a link
.btn-link {
color: @link-color;
font-weight: normal;
border-radius: 0;
&,
&:active,
&.active,
&[disabled],
fieldset[disabled] & {
background-color: transparent;
.box-shadow(none);
}
&,
&:hover,
&:focus,
&:active {
border-color: transparent;
}
&:hover,
&:focus {
color: @link-hover-color;
text-decoration: @link-hover-decoration;
background-color: transparent;
}
&[disabled],
fieldset[disabled] & {
&:hover,
&:focus {
color: @btn-link-disabled-color;
text-decoration: none;
}
}
}
// Button Sizes
// --------------------------------------------------
.btn-lg {
// line-height: ensure even-numbered height of button next to large input
.button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @btn-border-radius-large);
}
.btn-sm {
// line-height: ensure proper height of button next to small input
.button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);
}
.btn-xs {
.button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);
}
// Block button
// --------------------------------------------------
.btn-block {
display: block;
width: 100%;
}
// Vertically space out multiple block buttons
.btn-block + .btn-block {
margin-top: 5px;
}
// Specificity overrides
input[type="submit"],
input[type="reset"],
input[type="button"] {
&.btn-block {
width: 100%;
}
}
@@ -1,270 +0,0 @@
//
// Carousel
// --------------------------------------------------
// Wrapper for the slide container and indicators
.carousel {
position: relative;
}
.carousel-inner {
position: relative;
overflow: hidden;
width: 100%;
> .item {
display: none;
position: relative;
.transition(.6s ease-in-out left);
// Account for jankitude on images
> img,
> a > img {
&:extend(.img-responsive);
line-height: 1;
}
// WebKit CSS3 transforms for supported devices
@media all and (transform-3d), (-webkit-transform-3d) {
.transition-transform(~'0.6s ease-in-out');
.backface-visibility(~'hidden');
.perspective(1000px);
&.next,
&.active.right {
.translate3d(100%, 0, 0);
left: 0;
}
&.prev,
&.active.left {
.translate3d(-100%, 0, 0);
left: 0;
}
&.next.left,
&.prev.right,
&.active {
.translate3d(0, 0, 0);
left: 0;
}
}
}
> .active,
> .next,
> .prev {
display: block;
}
> .active {
left: 0;
}
> .next,
> .prev {
position: absolute;
top: 0;
width: 100%;
}
> .next {
left: 100%;
}
> .prev {
left: -100%;
}
> .next.left,
> .prev.right {
left: 0;
}
> .active.left {
left: -100%;
}
> .active.right {
left: 100%;
}
}
// Left/right controls for nav
// ---------------------------
.carousel-control {
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: @carousel-control-width;
.opacity(@carousel-control-opacity);
font-size: @carousel-control-font-size;
color: @carousel-control-color;
text-align: center;
text-shadow: @carousel-text-shadow;
background-color: rgba(0, 0, 0, 0); // Fix IE9 click-thru bug
// We can't have this transition here because WebKit cancels the carousel
// animation if you trip this while in the middle of another animation.
// Set gradients for backgrounds
&.left {
#gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001));
}
&.right {
left: auto;
right: 0;
#gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5));
}
// Hover/focus state
&:hover,
&:focus {
outline: 0;
color: @carousel-control-color;
text-decoration: none;
.opacity(.9);
}
// Toggles
.icon-prev,
.icon-next,
.glyphicon-chevron-left,
.glyphicon-chevron-right {
position: absolute;
top: 50%;
margin-top: -10px;
z-index: 5;
display: inline-block;
}
.icon-prev,
.glyphicon-chevron-left {
left: 50%;
margin-left: -10px;
}
.icon-next,
.glyphicon-chevron-right {
right: 50%;
margin-right: -10px;
}
.icon-prev,
.icon-next {
width: 20px;
height: 20px;
line-height: 1;
font-family: serif;
}
.icon-prev {
&:before {
content: '\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039)
}
}
.icon-next {
&:before {
content: '\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A)
}
}
}
// Optional indicator pips
//
// Add an unordered list with the following class and add a list item for each
// slide your carousel holds.
.carousel-indicators {
position: absolute;
bottom: 10px;
left: 50%;
z-index: 15;
width: 60%;
margin-left: -30%;
padding-left: 0;
list-style: none;
text-align: center;
li {
display: inline-block;
width: 10px;
height: 10px;
margin: 1px;
text-indent: -999px;
border: 1px solid @carousel-indicator-border-color;
border-radius: 10px;
cursor: pointer;
// IE8-9 hack for event handling
//
// Internet Explorer 8-9 does not support clicks on elements without a set
// `background-color`. We cannot use `filter` since that's not viewed as a
// background color by the browser. Thus, a hack is needed.
// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Internet_Explorer
//
// For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we
// set alpha transparency for the best results possible.
background-color: #000 \9; // IE8
background-color: rgba(0,0,0,0); // IE9
}
.active {
margin: 0;
width: 12px;
height: 12px;
background-color: @carousel-indicator-active-bg;
}
}
// Optional captions
// -----------------------------
// Hidden by default for smaller viewports
.carousel-caption {
position: absolute;
left: 15%;
right: 15%;
bottom: 20px;
z-index: 10;
padding-top: 20px;
padding-bottom: 20px;
color: @carousel-caption-color;
text-align: center;
text-shadow: @carousel-text-shadow;
& .btn {
text-shadow: none; // No shadow for button elements in carousel-caption
}
}
// Scale up controls for tablets and up
@media screen and (min-width: @screen-sm-min) {
// Scale up the controls a smidge
.carousel-control {
.glyphicon-chevron-left,
.glyphicon-chevron-right,
.icon-prev,
.icon-next {
width: (@carousel-control-font-size * 1.5);
height: (@carousel-control-font-size * 1.5);
margin-top: (@carousel-control-font-size / -2);
font-size: (@carousel-control-font-size * 1.5);
}
.glyphicon-chevron-left,
.icon-prev {
margin-left: (@carousel-control-font-size / -2);
}
.glyphicon-chevron-right,
.icon-next {
margin-right: (@carousel-control-font-size / -2);
}
}
// Show and left align the captions
.carousel-caption {
left: 20%;
right: 20%;
padding-bottom: 30px;
}
// Move up the indicators
.carousel-indicators {
bottom: 20px;
}
}
@@ -1,34 +0,0 @@
//
// Close icons
// --------------------------------------------------
.close {
float: right;
font-size: (@font-size-base * 1.5);
font-weight: @close-font-weight;
line-height: 1;
color: @close-color;
text-shadow: @close-text-shadow;
.opacity(.2);
&:hover,
&:focus {
color: @close-color;
text-decoration: none;
cursor: pointer;
.opacity(.5);
}
// Additional properties for button version
// iOS requires the button element instead of an anchor tag.
// If you want the anchor version, it requires `href="#"`.
// See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
button& {
padding: 0;
cursor: pointer;
background: transparent;
border: 0;
-webkit-appearance: none;
}
}
@@ -1,69 +0,0 @@
//
// Code (inline and block)
// --------------------------------------------------
// Inline and block code styles
code,
kbd,
pre,
samp {
font-family: @font-family-monospace;
}
// Inline code
code {
padding: 2px 4px;
font-size: 90%;
color: @code-color;
background-color: @code-bg;
border-radius: @border-radius-base;
}
// User input typically entered via keyboard
kbd {
padding: 2px 4px;
font-size: 90%;
color: @kbd-color;
background-color: @kbd-bg;
border-radius: @border-radius-small;
box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);
kbd {
padding: 0;
font-size: 100%;
font-weight: bold;
box-shadow: none;
}
}
// Blocks of code
pre {
display: block;
padding: ((@line-height-computed - 1) / 2);
margin: 0 0 (@line-height-computed / 2);
font-size: (@font-size-base - 1); // 14px to 13px
line-height: @line-height-base;
word-break: break-all;
word-wrap: break-word;
color: @pre-color;
background-color: @pre-bg;
border: 1px solid @pre-border-color;
border-radius: @border-radius-base;
// Account for some code outputs that place code tags in pre tags
code {
padding: 0;
font-size: inherit;
color: inherit;
white-space: pre-wrap;
background-color: transparent;
border-radius: 0;
}
}
// Enable scrollable blocks of code
.pre-scrollable {
max-height: @pre-scrollable-max-height;
overflow-y: scroll;
}
@@ -1,33 +0,0 @@
//
// Component animations
// --------------------------------------------------
// Heads up!
//
// We don't use the `.opacity()` mixin here since it causes a bug with text
// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552.
.fade {
opacity: 0;
.transition(opacity .15s linear);
&.in {
opacity: 1;
}
}
.collapse {
display: none;
&.in { display: block; }
tr&.in { display: table-row; }
tbody&.in { display: table-row-group; }
}
.collapsing {
position: relative;
height: 0;
overflow: hidden;
.transition-property(~"height, visibility");
.transition-duration(.35s);
.transition-timing-function(ease);
}
@@ -1,216 +0,0 @@
//
// Dropdown menus
// --------------------------------------------------
// Dropdown arrow/caret
.caret {
display: inline-block;
width: 0;
height: 0;
margin-left: 2px;
vertical-align: middle;
border-top: @caret-width-base dashed;
border-top: @caret-width-base solid ~"\9"; // IE8
border-right: @caret-width-base solid transparent;
border-left: @caret-width-base solid transparent;
}
// The dropdown wrapper (div)
.dropup,
.dropdown {
position: relative;
}
// Prevent the focus on the dropdown toggle when closing dropdowns
.dropdown-toggle:focus {
outline: 0;
}
// The dropdown menu (ul)
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
z-index: @zindex-dropdown;
display: none; // none by default, but block on "open" of the menu
float: left;
min-width: 160px;
padding: 5px 0;
margin: 2px 0 0; // override default ul
list-style: none;
font-size: @font-size-base;
text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)
background-color: @dropdown-bg;
border: 1px solid @dropdown-fallback-border; // IE8 fallback
border: 1px solid @dropdown-border;
border-radius: @border-radius-base;
.box-shadow(0 6px 12px rgba(0,0,0,.175));
background-clip: padding-box;
// Aligns the dropdown menu to right
//
// Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`
&.pull-right {
right: 0;
left: auto;
}
// Dividers (basically an hr) within the dropdown
.divider {
.nav-divider(@dropdown-divider-bg);
}
// Links within the dropdown menu
> li > a {
display: block;
padding: 3px 20px;
clear: both;
font-weight: normal;
line-height: @line-height-base;
color: @dropdown-link-color;
white-space: nowrap; // prevent links from randomly breaking onto new lines
}
}
// Hover/Focus state
.dropdown-menu > li > a {
&:hover,
&:focus {
text-decoration: none;
color: @dropdown-link-hover-color;
background-color: @dropdown-link-hover-bg;
}
}
// Active state
.dropdown-menu > .active > a {
&,
&:hover,
&:focus {
color: @dropdown-link-active-color;
text-decoration: none;
outline: 0;
background-color: @dropdown-link-active-bg;
}
}
// Disabled state
//
// Gray out text and ensure the hover/focus state remains gray
.dropdown-menu > .disabled > a {
&,
&:hover,
&:focus {
color: @dropdown-link-disabled-color;
}
// Nuke hover/focus effects
&:hover,
&:focus {
text-decoration: none;
background-color: transparent;
background-image: none; // Remove CSS gradient
.reset-filter();
cursor: @cursor-disabled;
}
}
// Open state for the dropdown
.open {
// Show the menu
> .dropdown-menu {
display: block;
}
// Remove the outline when :focus is triggered
> a {
outline: 0;
}
}
// Menu positioning
//
// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown
// menu with the parent.
.dropdown-menu-right {
left: auto; // Reset the default from `.dropdown-menu`
right: 0;
}
// With v3, we enabled auto-flipping if you have a dropdown within a right
// aligned nav component. To enable the undoing of that, we provide an override
// to restore the default dropdown menu alignment.
//
// This is only for left-aligning a dropdown menu within a `.navbar-right` or
// `.pull-right` nav component.
.dropdown-menu-left {
left: 0;
right: auto;
}
// Dropdown section headers
.dropdown-header {
display: block;
padding: 3px 20px;
font-size: @font-size-small;
line-height: @line-height-base;
color: @dropdown-header-color;
white-space: nowrap; // as with > li > a
}
// Backdrop to catch body clicks on mobile, etc.
.dropdown-backdrop {
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
z-index: (@zindex-dropdown - 10);
}
// Right aligned dropdowns
.pull-right > .dropdown-menu {
right: 0;
left: auto;
}
// Allow for dropdowns to go bottom up (aka, dropup-menu)
//
// Just add .dropup after the standard .dropdown class and you're set, bro.
// TODO: abstract this so that the navbar fixed styles are not placed here?
.dropup,
.navbar-fixed-bottom .dropdown {
// Reverse the caret
.caret {
border-top: 0;
border-bottom: @caret-width-base dashed;
border-bottom: @caret-width-base solid ~"\9"; // IE8
content: "";
}
// Different positioning for bottom up menu
.dropdown-menu {
top: auto;
bottom: 100%;
margin-bottom: 2px;
}
}
// Component alignment
//
// Reiterate per navbar.less and the modified component alignment there.
@media (min-width: @grid-float-breakpoint) {
.navbar-right {
.dropdown-menu {
.dropdown-menu-right();
}
// Necessary for overrides of the default right aligned menu.
// Will remove come v4 in all likelihood.
.dropdown-menu-left {
.dropdown-menu-left();
}
}
}
@@ -1,613 +0,0 @@
//
// Forms
// --------------------------------------------------
// Normalize non-controls
//
// Restyle and baseline non-control form elements.
fieldset {
padding: 0;
margin: 0;
border: 0;
// Chrome and Firefox set a `min-width: min-content;` on fieldsets,
// so we reset that to ensure it behaves more like a standard block element.
// See https://github.com/twbs/bootstrap/issues/12359.
min-width: 0;
}
legend {
display: block;
width: 100%;
padding: 0;
margin-bottom: @line-height-computed;
font-size: (@font-size-base * 1.5);
line-height: inherit;
color: @legend-color;
border: 0;
border-bottom: 1px solid @legend-border-color;
}
label {
display: inline-block;
max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)
margin-bottom: 5px;
font-weight: bold;
}
// Normalize form controls
//
// While most of our form styles require extra classes, some basic normalization
// is required to ensure optimum display with or without those classes to better
// address browser inconsistencies.
// Override content-box in Normalize (* isn't specific enough)
input[type="search"] {
.box-sizing(border-box);
}
// Position radios and checkboxes better
input[type="radio"],
input[type="checkbox"] {
margin: 4px 0 0;
margin-top: 1px \9; // IE8-9
line-height: normal;
}
input[type="file"] {
display: block;
}
// Make range inputs behave like textual form controls
input[type="range"] {
display: block;
width: 100%;
}
// Make multiple select elements height not fixed
select[multiple],
select[size] {
height: auto;
}
// Focus for file, radio, and checkbox
input[type="file"]:focus,
input[type="radio"]:focus,
input[type="checkbox"]:focus {
.tab-focus();
}
// Adjust output element
output {
display: block;
padding-top: (@padding-base-vertical + 1);
font-size: @font-size-base;
line-height: @line-height-base;
color: @input-color;
}
// Common form controls
//
// Shared size and type resets for form controls. Apply `.form-control` to any
// of the following form controls:
//
// select
// textarea
// input[type="text"]
// input[type="password"]
// input[type="datetime"]
// input[type="datetime-local"]
// input[type="date"]
// input[type="month"]
// input[type="time"]
// input[type="week"]
// input[type="number"]
// input[type="email"]
// input[type="url"]
// input[type="search"]
// input[type="tel"]
// input[type="color"]
.form-control {
display: block;
width: 100%;
height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)
padding: @padding-base-vertical @padding-base-horizontal;
font-size: @font-size-base;
line-height: @line-height-base;
color: @input-color;
background-color: @input-bg;
background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214
border: 1px solid @input-border;
border-radius: @input-border-radius; // Note: This has no effect on <select>s in some browsers, due to the limited stylability of <select>s in CSS.
.box-shadow(inset 0 1px 1px rgba(0,0,0,.075));
.transition(~"border-color ease-in-out .15s, box-shadow ease-in-out .15s");
// Customize the `:focus` state to imitate native WebKit styles.
.form-control-focus();
// Placeholder
.placeholder();
// Unstyle the caret on `<select>`s in IE10+.
&::-ms-expand {
border: 0;
background-color: transparent;
}
// Disabled and read-only inputs
//
// HTML5 says that controls under a fieldset > legend:first-child won't be
// disabled if the fieldset is disabled. Due to implementation difficulty, we
// don't honor that edge case; we style them as disabled anyway.
&[disabled],
&[readonly],
fieldset[disabled] & {
background-color: @input-bg-disabled;
opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655
}
&[disabled],
fieldset[disabled] & {
cursor: @cursor-disabled;
}
// Reset height for `textarea`s
textarea& {
height: auto;
}
}
// Search inputs in iOS
//
// This overrides the extra rounded corners on search inputs in iOS so that our
// `.form-control` class can properly style them. Note that this cannot simply
// be added to `.form-control` as it's not specific enough. For details, see
// https://github.com/twbs/bootstrap/issues/11586.
input[type="search"] {
-webkit-appearance: none;
}
// Special styles for iOS temporal inputs
//
// In Mobile Safari, setting `display: block` on temporal inputs causes the
// text within the input to become vertically misaligned. As a workaround, we
// set a pixel line-height that matches the given height of the input, but only
// for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848
//
// Note that as of 8.3, iOS doesn't support `datetime` or `week`.
@media screen and (-webkit-min-device-pixel-ratio: 0) {
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
&.form-control {
line-height: @input-height-base;
}
&.input-sm,
.input-group-sm & {
line-height: @input-height-small;
}
&.input-lg,
.input-group-lg & {
line-height: @input-height-large;
}
}
}
// Form groups
//
// Designed to help with the organization and spacing of vertical forms. For
// horizontal forms, use the predefined grid classes.
.form-group {
margin-bottom: @form-group-margin-bottom;
}
// Checkboxes and radios
//
// Indent the labels to position radios/checkboxes as hanging controls.
.radio,
.checkbox {
position: relative;
display: block;
margin-top: 10px;
margin-bottom: 10px;
label {
min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text
padding-left: 20px;
margin-bottom: 0;
font-weight: normal;
cursor: pointer;
}
}
.radio input[type="radio"],
.radio-inline input[type="radio"],
.checkbox input[type="checkbox"],
.checkbox-inline input[type="checkbox"] {
position: absolute;
margin-left: -20px;
margin-top: 4px \9;
}
.radio + .radio,
.checkbox + .checkbox {
margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing
}
// Radios and checkboxes on same line
.radio-inline,
.checkbox-inline {
position: relative;
display: inline-block;
padding-left: 20px;
margin-bottom: 0;
vertical-align: middle;
font-weight: normal;
cursor: pointer;
}
.radio-inline + .radio-inline,
.checkbox-inline + .checkbox-inline {
margin-top: 0;
margin-left: 10px; // space out consecutive inline controls
}
// Apply same disabled cursor tweak as for inputs
// Some special care is needed because <label>s don't inherit their parent's `cursor`.
//
// Note: Neither radios nor checkboxes can be readonly.
input[type="radio"],
input[type="checkbox"] {
&[disabled],
&.disabled,
fieldset[disabled] & {
cursor: @cursor-disabled;
}
}
// These classes are used directly on <label>s
.radio-inline,
.checkbox-inline {
&.disabled,
fieldset[disabled] & {
cursor: @cursor-disabled;
}
}
// These classes are used on elements with <label> descendants
.radio,
.checkbox {
&.disabled,
fieldset[disabled] & {
label {
cursor: @cursor-disabled;
}
}
}
// Static form control text
//
// Apply class to a `p` element to make any string of text align with labels in
// a horizontal form layout.
.form-control-static {
// Size it appropriately next to real form controls
padding-top: (@padding-base-vertical + 1);
padding-bottom: (@padding-base-vertical + 1);
// Remove default margin from `p`
margin-bottom: 0;
min-height: (@line-height-computed + @font-size-base);
&.input-lg,
&.input-sm {
padding-left: 0;
padding-right: 0;
}
}
// Form control sizing
//
// Build on `.form-control` with modifier classes to decrease or increase the
// height and font-size of form controls.
//
// The `.form-group-* form-control` variations are sadly duplicated to avoid the
// issue documented in https://github.com/twbs/bootstrap/issues/15074.
.input-sm {
.input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @input-border-radius-small);
}
.form-group-sm {
.form-control {
height: @input-height-small;
padding: @padding-small-vertical @padding-small-horizontal;
font-size: @font-size-small;
line-height: @line-height-small;
border-radius: @input-border-radius-small;
}
select.form-control {
height: @input-height-small;
line-height: @input-height-small;
}
textarea.form-control,
select[multiple].form-control {
height: auto;
}
.form-control-static {
height: @input-height-small;
min-height: (@line-height-computed + @font-size-small);
padding: (@padding-small-vertical + 1) @padding-small-horizontal;
font-size: @font-size-small;
line-height: @line-height-small;
}
}
.input-lg {
.input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @input-border-radius-large);
}
.form-group-lg {
.form-control {
height: @input-height-large;
padding: @padding-large-vertical @padding-large-horizontal;
font-size: @font-size-large;
line-height: @line-height-large;
border-radius: @input-border-radius-large;
}
select.form-control {
height: @input-height-large;
line-height: @input-height-large;
}
textarea.form-control,
select[multiple].form-control {
height: auto;
}
.form-control-static {
height: @input-height-large;
min-height: (@line-height-computed + @font-size-large);
padding: (@padding-large-vertical + 1) @padding-large-horizontal;
font-size: @font-size-large;
line-height: @line-height-large;
}
}
// Form control feedback states
//
// Apply contextual and semantic states to individual form controls.
.has-feedback {
// Enable absolute positioning
position: relative;
// Ensure icons don't overlap text
.form-control {
padding-right: (@input-height-base * 1.25);
}
}
// Feedback icon (requires .glyphicon classes)
.form-control-feedback {
position: absolute;
top: 0;
right: 0;
z-index: 2; // Ensure icon is above input groups
display: block;
width: @input-height-base;
height: @input-height-base;
line-height: @input-height-base;
text-align: center;
pointer-events: none;
}
.input-lg + .form-control-feedback,
.input-group-lg + .form-control-feedback,
.form-group-lg .form-control + .form-control-feedback {
width: @input-height-large;
height: @input-height-large;
line-height: @input-height-large;
}
.input-sm + .form-control-feedback,
.input-group-sm + .form-control-feedback,
.form-group-sm .form-control + .form-control-feedback {
width: @input-height-small;
height: @input-height-small;
line-height: @input-height-small;
}
// Feedback states
.has-success {
.form-control-validation(@state-success-text; @state-success-text; @state-success-bg);
}
.has-warning {
.form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg);
}
.has-error {
.form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg);
}
// Reposition feedback icon if input has visible label above
.has-feedback label {
& ~ .form-control-feedback {
top: (@line-height-computed + 5); // Height of the `label` and its margin
}
&.sr-only ~ .form-control-feedback {
top: 0;
}
}
// Help text
//
// Apply to any element you wish to create light text for placement immediately
// below a form control. Use for general help, formatting, or instructional text.
.help-block {
display: block; // account for any element using help-block
margin-top: 5px;
margin-bottom: 10px;
color: lighten(@text-color, 25%); // lighten the text some for contrast
}
// Inline forms
//
// Make forms appear inline(-block) by adding the `.form-inline` class. Inline
// forms begin stacked on extra small (mobile) devices and then go inline when
// viewports reach <768px.
//
// Requires wrapping inputs and labels with `.form-group` for proper display of
// default HTML form controls and our custom form controls (e.g., input groups).
//
// Heads up! This is mixin-ed into `.navbar-form` in navbars.less.
.form-inline {
// Kick in the inline
@media (min-width: @screen-sm-min) {
// Inline-block all the things for "inline"
.form-group {
display: inline-block;
margin-bottom: 0;
vertical-align: middle;
}
// In navbar-form, allow folks to *not* use `.form-group`
.form-control {
display: inline-block;
width: auto; // Prevent labels from stacking above inputs in `.form-group`
vertical-align: middle;
}
// Make static controls behave like regular ones
.form-control-static {
display: inline-block;
}
.input-group {
display: inline-table;
vertical-align: middle;
.input-group-addon,
.input-group-btn,
.form-control {
width: auto;
}
}
// Input groups need that 100% width though
.input-group > .form-control {
width: 100%;
}
.control-label {
margin-bottom: 0;
vertical-align: middle;
}
// Remove default margin on radios/checkboxes that were used for stacking, and
// then undo the floating of radios and checkboxes to match.
.radio,
.checkbox {
display: inline-block;
margin-top: 0;
margin-bottom: 0;
vertical-align: middle;
label {
padding-left: 0;
}
}
.radio input[type="radio"],
.checkbox input[type="checkbox"] {
position: relative;
margin-left: 0;
}
// Re-override the feedback icon.
.has-feedback .form-control-feedback {
top: 0;
}
}
}
// Horizontal forms
//
// Horizontal forms are built on grid classes and allow you to create forms with
// labels on the left and inputs on the right.
.form-horizontal {
// Consistent vertical alignment of radios and checkboxes
//
// Labels also get some reset styles, but that is scoped to a media query below.
.radio,
.checkbox,
.radio-inline,
.checkbox-inline {
margin-top: 0;
margin-bottom: 0;
padding-top: (@padding-base-vertical + 1); // Default padding plus a border
}
// Account for padding we're adding to ensure the alignment and of help text
// and other content below items
.radio,
.checkbox {
min-height: (@line-height-computed + (@padding-base-vertical + 1));
}
// Make form groups behave like rows
.form-group {
.make-row();
}
// Reset spacing and right align labels, but scope to media queries so that
// labels on narrow viewports stack the same as a default form example.
@media (min-width: @screen-sm-min) {
.control-label {
text-align: right;
margin-bottom: 0;
padding-top: (@padding-base-vertical + 1); // Default padding plus a border
}
}
// Validation states
//
// Reposition the icon because it's now within a grid column and columns have
// `position: relative;` on them. Also accounts for the grid gutter padding.
.has-feedback .form-control-feedback {
right: floor((@grid-gutter-width / 2));
}
// Form group sizes
//
// Quick utility class for applying `.input-lg` and `.input-sm` styles to the
// inputs and labels within a `.form-group`.
.form-group-lg {
@media (min-width: @screen-sm-min) {
.control-label {
padding-top: (@padding-large-vertical + 1);
font-size: @font-size-large;
}
}
}
.form-group-sm {
@media (min-width: @screen-sm-min) {
.control-label {
padding-top: (@padding-small-vertical + 1);
font-size: @font-size-small;
}
}
}
}
@@ -1,305 +0,0 @@
//
// Glyphicons for Bootstrap
//
// Since icons are fonts, they can be placed anywhere text is placed and are
// thus automatically sized to match the surrounding child. To use, create an
// inline element with the appropriate classes, like so:
//
// <a href="#"><span class="glyphicon glyphicon-star"></span> Star</a>
// Import the fonts
@font-face {
font-family: 'Glyphicons Halflings';
src: url('@{icon-font-path}@{icon-font-name}.eot');
src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),
url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'),
url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),
url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),
url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');
}
// Catchall baseclass
.glyphicon {
position: relative;
top: 1px;
display: inline-block;
font-family: 'Glyphicons Halflings';
font-style: normal;
font-weight: normal;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
// Individual icons
.glyphicon-asterisk { &:before { content: "\002a"; } }
.glyphicon-plus { &:before { content: "\002b"; } }
.glyphicon-euro,
.glyphicon-eur { &:before { content: "\20ac"; } }
.glyphicon-minus { &:before { content: "\2212"; } }
.glyphicon-cloud { &:before { content: "\2601"; } }
.glyphicon-envelope { &:before { content: "\2709"; } }
.glyphicon-pencil { &:before { content: "\270f"; } }
.glyphicon-glass { &:before { content: "\e001"; } }
.glyphicon-music { &:before { content: "\e002"; } }
.glyphicon-search { &:before { content: "\e003"; } }
.glyphicon-heart { &:before { content: "\e005"; } }
.glyphicon-star { &:before { content: "\e006"; } }
.glyphicon-star-empty { &:before { content: "\e007"; } }
.glyphicon-user { &:before { content: "\e008"; } }
.glyphicon-film { &:before { content: "\e009"; } }
.glyphicon-th-large { &:before { content: "\e010"; } }
.glyphicon-th { &:before { content: "\e011"; } }
.glyphicon-th-list { &:before { content: "\e012"; } }
.glyphicon-ok { &:before { content: "\e013"; } }
.glyphicon-remove { &:before { content: "\e014"; } }
.glyphicon-zoom-in { &:before { content: "\e015"; } }
.glyphicon-zoom-out { &:before { content: "\e016"; } }
.glyphicon-off { &:before { content: "\e017"; } }
.glyphicon-signal { &:before { content: "\e018"; } }
.glyphicon-cog { &:before { content: "\e019"; } }
.glyphicon-trash { &:before { content: "\e020"; } }
.glyphicon-home { &:before { content: "\e021"; } }
.glyphicon-file { &:before { content: "\e022"; } }
.glyphicon-time { &:before { content: "\e023"; } }
.glyphicon-road { &:before { content: "\e024"; } }
.glyphicon-download-alt { &:before { content: "\e025"; } }
.glyphicon-download { &:before { content: "\e026"; } }
.glyphicon-upload { &:before { content: "\e027"; } }
.glyphicon-inbox { &:before { content: "\e028"; } }
.glyphicon-play-circle { &:before { content: "\e029"; } }
.glyphicon-repeat { &:before { content: "\e030"; } }
.glyphicon-refresh { &:before { content: "\e031"; } }
.glyphicon-list-alt { &:before { content: "\e032"; } }
.glyphicon-lock { &:before { content: "\e033"; } }
.glyphicon-flag { &:before { content: "\e034"; } }
.glyphicon-headphones { &:before { content: "\e035"; } }
.glyphicon-volume-off { &:before { content: "\e036"; } }
.glyphicon-volume-down { &:before { content: "\e037"; } }
.glyphicon-volume-up { &:before { content: "\e038"; } }
.glyphicon-qrcode { &:before { content: "\e039"; } }
.glyphicon-barcode { &:before { content: "\e040"; } }
.glyphicon-tag { &:before { content: "\e041"; } }
.glyphicon-tags { &:before { content: "\e042"; } }
.glyphicon-book { &:before { content: "\e043"; } }
.glyphicon-bookmark { &:before { content: "\e044"; } }
.glyphicon-print { &:before { content: "\e045"; } }
.glyphicon-camera { &:before { content: "\e046"; } }
.glyphicon-font { &:before { content: "\e047"; } }
.glyphicon-bold { &:before { content: "\e048"; } }
.glyphicon-italic { &:before { content: "\e049"; } }
.glyphicon-text-height { &:before { content: "\e050"; } }
.glyphicon-text-width { &:before { content: "\e051"; } }
.glyphicon-align-left { &:before { content: "\e052"; } }
.glyphicon-align-center { &:before { content: "\e053"; } }
.glyphicon-align-right { &:before { content: "\e054"; } }
.glyphicon-align-justify { &:before { content: "\e055"; } }
.glyphicon-list { &:before { content: "\e056"; } }
.glyphicon-indent-left { &:before { content: "\e057"; } }
.glyphicon-indent-right { &:before { content: "\e058"; } }
.glyphicon-facetime-video { &:before { content: "\e059"; } }
.glyphicon-picture { &:before { content: "\e060"; } }
.glyphicon-map-marker { &:before { content: "\e062"; } }
.glyphicon-adjust { &:before { content: "\e063"; } }
.glyphicon-tint { &:before { content: "\e064"; } }
.glyphicon-edit { &:before { content: "\e065"; } }
.glyphicon-share { &:before { content: "\e066"; } }
.glyphicon-check { &:before { content: "\e067"; } }
.glyphicon-move { &:before { content: "\e068"; } }
.glyphicon-step-backward { &:before { content: "\e069"; } }
.glyphicon-fast-backward { &:before { content: "\e070"; } }
.glyphicon-backward { &:before { content: "\e071"; } }
.glyphicon-play { &:before { content: "\e072"; } }
.glyphicon-pause { &:before { content: "\e073"; } }
.glyphicon-stop { &:before { content: "\e074"; } }
.glyphicon-forward { &:before { content: "\e075"; } }
.glyphicon-fast-forward { &:before { content: "\e076"; } }
.glyphicon-step-forward { &:before { content: "\e077"; } }
.glyphicon-eject { &:before { content: "\e078"; } }
.glyphicon-chevron-left { &:before { content: "\e079"; } }
.glyphicon-chevron-right { &:before { content: "\e080"; } }
.glyphicon-plus-sign { &:before { content: "\e081"; } }
.glyphicon-minus-sign { &:before { content: "\e082"; } }
.glyphicon-remove-sign { &:before { content: "\e083"; } }
.glyphicon-ok-sign { &:before { content: "\e084"; } }
.glyphicon-question-sign { &:before { content: "\e085"; } }
.glyphicon-info-sign { &:before { content: "\e086"; } }
.glyphicon-screenshot { &:before { content: "\e087"; } }
.glyphicon-remove-circle { &:before { content: "\e088"; } }
.glyphicon-ok-circle { &:before { content: "\e089"; } }
.glyphicon-ban-circle { &:before { content: "\e090"; } }
.glyphicon-arrow-left { &:before { content: "\e091"; } }
.glyphicon-arrow-right { &:before { content: "\e092"; } }
.glyphicon-arrow-up { &:before { content: "\e093"; } }
.glyphicon-arrow-down { &:before { content: "\e094"; } }
.glyphicon-share-alt { &:before { content: "\e095"; } }
.glyphicon-resize-full { &:before { content: "\e096"; } }
.glyphicon-resize-small { &:before { content: "\e097"; } }
.glyphicon-exclamation-sign { &:before { content: "\e101"; } }
.glyphicon-gift { &:before { content: "\e102"; } }
.glyphicon-leaf { &:before { content: "\e103"; } }
.glyphicon-fire { &:before { content: "\e104"; } }
.glyphicon-eye-open { &:before { content: "\e105"; } }
.glyphicon-eye-close { &:before { content: "\e106"; } }
.glyphicon-warning-sign { &:before { content: "\e107"; } }
.glyphicon-plane { &:before { content: "\e108"; } }
.glyphicon-calendar { &:before { content: "\e109"; } }
.glyphicon-random { &:before { content: "\e110"; } }
.glyphicon-comment { &:before { content: "\e111"; } }
.glyphicon-magnet { &:before { content: "\e112"; } }
.glyphicon-chevron-up { &:before { content: "\e113"; } }
.glyphicon-chevron-down { &:before { content: "\e114"; } }
.glyphicon-retweet { &:before { content: "\e115"; } }
.glyphicon-shopping-cart { &:before { content: "\e116"; } }
.glyphicon-folder-close { &:before { content: "\e117"; } }
.glyphicon-folder-open { &:before { content: "\e118"; } }
.glyphicon-resize-vertical { &:before { content: "\e119"; } }
.glyphicon-resize-horizontal { &:before { content: "\e120"; } }
.glyphicon-hdd { &:before { content: "\e121"; } }
.glyphicon-bullhorn { &:before { content: "\e122"; } }
.glyphicon-bell { &:before { content: "\e123"; } }
.glyphicon-certificate { &:before { content: "\e124"; } }
.glyphicon-thumbs-up { &:before { content: "\e125"; } }
.glyphicon-thumbs-down { &:before { content: "\e126"; } }
.glyphicon-hand-right { &:before { content: "\e127"; } }
.glyphicon-hand-left { &:before { content: "\e128"; } }
.glyphicon-hand-up { &:before { content: "\e129"; } }
.glyphicon-hand-down { &:before { content: "\e130"; } }
.glyphicon-circle-arrow-right { &:before { content: "\e131"; } }
.glyphicon-circle-arrow-left { &:before { content: "\e132"; } }
.glyphicon-circle-arrow-up { &:before { content: "\e133"; } }
.glyphicon-circle-arrow-down { &:before { content: "\e134"; } }
.glyphicon-globe { &:before { content: "\e135"; } }
.glyphicon-wrench { &:before { content: "\e136"; } }
.glyphicon-tasks { &:before { content: "\e137"; } }
.glyphicon-filter { &:before { content: "\e138"; } }
.glyphicon-briefcase { &:before { content: "\e139"; } }
.glyphicon-fullscreen { &:before { content: "\e140"; } }
.glyphicon-dashboard { &:before { content: "\e141"; } }
.glyphicon-paperclip { &:before { content: "\e142"; } }
.glyphicon-heart-empty { &:before { content: "\e143"; } }
.glyphicon-link { &:before { content: "\e144"; } }
.glyphicon-phone { &:before { content: "\e145"; } }
.glyphicon-pushpin { &:before { content: "\e146"; } }
.glyphicon-usd { &:before { content: "\e148"; } }
.glyphicon-gbp { &:before { content: "\e149"; } }
.glyphicon-sort { &:before { content: "\e150"; } }
.glyphicon-sort-by-alphabet { &:before { content: "\e151"; } }
.glyphicon-sort-by-alphabet-alt { &:before { content: "\e152"; } }
.glyphicon-sort-by-order { &:before { content: "\e153"; } }
.glyphicon-sort-by-order-alt { &:before { content: "\e154"; } }
.glyphicon-sort-by-attributes { &:before { content: "\e155"; } }
.glyphicon-sort-by-attributes-alt { &:before { content: "\e156"; } }
.glyphicon-unchecked { &:before { content: "\e157"; } }
.glyphicon-expand { &:before { content: "\e158"; } }
.glyphicon-collapse-down { &:before { content: "\e159"; } }
.glyphicon-collapse-up { &:before { content: "\e160"; } }
.glyphicon-log-in { &:before { content: "\e161"; } }
.glyphicon-flash { &:before { content: "\e162"; } }
.glyphicon-log-out { &:before { content: "\e163"; } }
.glyphicon-new-window { &:before { content: "\e164"; } }
.glyphicon-record { &:before { content: "\e165"; } }
.glyphicon-save { &:before { content: "\e166"; } }
.glyphicon-open { &:before { content: "\e167"; } }
.glyphicon-saved { &:before { content: "\e168"; } }
.glyphicon-import { &:before { content: "\e169"; } }
.glyphicon-export { &:before { content: "\e170"; } }
.glyphicon-send { &:before { content: "\e171"; } }
.glyphicon-floppy-disk { &:before { content: "\e172"; } }
.glyphicon-floppy-saved { &:before { content: "\e173"; } }
.glyphicon-floppy-remove { &:before { content: "\e174"; } }
.glyphicon-floppy-save { &:before { content: "\e175"; } }
.glyphicon-floppy-open { &:before { content: "\e176"; } }
.glyphicon-credit-card { &:before { content: "\e177"; } }
.glyphicon-transfer { &:before { content: "\e178"; } }
.glyphicon-cutlery { &:before { content: "\e179"; } }
.glyphicon-header { &:before { content: "\e180"; } }
.glyphicon-compressed { &:before { content: "\e181"; } }
.glyphicon-earphone { &:before { content: "\e182"; } }
.glyphicon-phone-alt { &:before { content: "\e183"; } }
.glyphicon-tower { &:before { content: "\e184"; } }
.glyphicon-stats { &:before { content: "\e185"; } }
.glyphicon-sd-video { &:before { content: "\e186"; } }
.glyphicon-hd-video { &:before { content: "\e187"; } }
.glyphicon-subtitles { &:before { content: "\e188"; } }
.glyphicon-sound-stereo { &:before { content: "\e189"; } }
.glyphicon-sound-dolby { &:before { content: "\e190"; } }
.glyphicon-sound-5-1 { &:before { content: "\e191"; } }
.glyphicon-sound-6-1 { &:before { content: "\e192"; } }
.glyphicon-sound-7-1 { &:before { content: "\e193"; } }
.glyphicon-copyright-mark { &:before { content: "\e194"; } }
.glyphicon-registration-mark { &:before { content: "\e195"; } }
.glyphicon-cloud-download { &:before { content: "\e197"; } }
.glyphicon-cloud-upload { &:before { content: "\e198"; } }
.glyphicon-tree-conifer { &:before { content: "\e199"; } }
.glyphicon-tree-deciduous { &:before { content: "\e200"; } }
.glyphicon-cd { &:before { content: "\e201"; } }
.glyphicon-save-file { &:before { content: "\e202"; } }
.glyphicon-open-file { &:before { content: "\e203"; } }
.glyphicon-level-up { &:before { content: "\e204"; } }
.glyphicon-copy { &:before { content: "\e205"; } }
.glyphicon-paste { &:before { content: "\e206"; } }
// The following 2 Glyphicons are omitted for the time being because
// they currently use Unicode codepoints that are outside the
// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle
// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.
// Notably, the bug affects some older versions of the Android Browser.
// More info: https://github.com/twbs/bootstrap/issues/10106
// .glyphicon-door { &:before { content: "\1f6aa"; } }
// .glyphicon-key { &:before { content: "\1f511"; } }
.glyphicon-alert { &:before { content: "\e209"; } }
.glyphicon-equalizer { &:before { content: "\e210"; } }
.glyphicon-king { &:before { content: "\e211"; } }
.glyphicon-queen { &:before { content: "\e212"; } }
.glyphicon-pawn { &:before { content: "\e213"; } }
.glyphicon-bishop { &:before { content: "\e214"; } }
.glyphicon-knight { &:before { content: "\e215"; } }
.glyphicon-baby-formula { &:before { content: "\e216"; } }
.glyphicon-tent { &:before { content: "\26fa"; } }
.glyphicon-blackboard { &:before { content: "\e218"; } }
.glyphicon-bed { &:before { content: "\e219"; } }
.glyphicon-apple { &:before { content: "\f8ff"; } }
.glyphicon-erase { &:before { content: "\e221"; } }
.glyphicon-hourglass { &:before { content: "\231b"; } }
.glyphicon-lamp { &:before { content: "\e223"; } }
.glyphicon-duplicate { &:before { content: "\e224"; } }
.glyphicon-piggy-bank { &:before { content: "\e225"; } }
.glyphicon-scissors { &:before { content: "\e226"; } }
.glyphicon-bitcoin { &:before { content: "\e227"; } }
.glyphicon-btc { &:before { content: "\e227"; } }
.glyphicon-xbt { &:before { content: "\e227"; } }
.glyphicon-yen { &:before { content: "\00a5"; } }
.glyphicon-jpy { &:before { content: "\00a5"; } }
.glyphicon-ruble { &:before { content: "\20bd"; } }
.glyphicon-rub { &:before { content: "\20bd"; } }
.glyphicon-scale { &:before { content: "\e230"; } }
.glyphicon-ice-lolly { &:before { content: "\e231"; } }
.glyphicon-ice-lolly-tasted { &:before { content: "\e232"; } }
.glyphicon-education { &:before { content: "\e233"; } }
.glyphicon-option-horizontal { &:before { content: "\e234"; } }
.glyphicon-option-vertical { &:before { content: "\e235"; } }
.glyphicon-menu-hamburger { &:before { content: "\e236"; } }
.glyphicon-modal-window { &:before { content: "\e237"; } }
.glyphicon-oil { &:before { content: "\e238"; } }
.glyphicon-grain { &:before { content: "\e239"; } }
.glyphicon-sunglasses { &:before { content: "\e240"; } }
.glyphicon-text-size { &:before { content: "\e241"; } }
.glyphicon-text-color { &:before { content: "\e242"; } }
.glyphicon-text-background { &:before { content: "\e243"; } }
.glyphicon-object-align-top { &:before { content: "\e244"; } }
.glyphicon-object-align-bottom { &:before { content: "\e245"; } }
.glyphicon-object-align-horizontal{ &:before { content: "\e246"; } }
.glyphicon-object-align-left { &:before { content: "\e247"; } }
.glyphicon-object-align-vertical { &:before { content: "\e248"; } }
.glyphicon-object-align-right { &:before { content: "\e249"; } }
.glyphicon-triangle-right { &:before { content: "\e250"; } }
.glyphicon-triangle-left { &:before { content: "\e251"; } }
.glyphicon-triangle-bottom { &:before { content: "\e252"; } }
.glyphicon-triangle-top { &:before { content: "\e253"; } }
.glyphicon-console { &:before { content: "\e254"; } }
.glyphicon-superscript { &:before { content: "\e255"; } }
.glyphicon-subscript { &:before { content: "\e256"; } }
.glyphicon-menu-left { &:before { content: "\e257"; } }
.glyphicon-menu-right { &:before { content: "\e258"; } }
.glyphicon-menu-down { &:before { content: "\e259"; } }
.glyphicon-menu-up { &:before { content: "\e260"; } }
@@ -1,84 +0,0 @@
//
// Grid system
// --------------------------------------------------
// Container widths
//
// Set the container width, and override it for fixed navbars in media queries.
.container {
.container-fixed();
@media (min-width: @screen-sm-min) {
width: @container-sm;
}
@media (min-width: @screen-md-min) {
width: @container-md;
}
@media (min-width: @screen-lg-min) {
width: @container-lg;
}
}
// Fluid container
//
// Utilizes the mixin meant for fixed width containers, but without any defined
// width for fluid, full width layouts.
.container-fluid {
.container-fixed();
}
// Row
//
// Rows contain and clear the floats of your columns.
.row {
.make-row();
}
// Columns
//
// Common styles for small and large grid columns
.make-grid-columns();
// Extra small grid
//
// Columns, offsets, pushes, and pulls for extra small devices like
// smartphones.
.make-grid(xs);
// Small grid
//
// Columns, offsets, pushes, and pulls for the small device range, from phones
// to tablets.
@media (min-width: @screen-sm-min) {
.make-grid(sm);
}
// Medium grid
//
// Columns, offsets, pushes, and pulls for the desktop device range.
@media (min-width: @screen-md-min) {
.make-grid(md);
}
// Large grid
//
// Columns, offsets, pushes, and pulls for the large desktop device range.
@media (min-width: @screen-lg-min) {
.make-grid(lg);
}
@@ -1,171 +0,0 @@
//
// Input groups
// --------------------------------------------------
// Base styles
// -------------------------
.input-group {
position: relative; // For dropdowns
display: table;
border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table
// Undo padding and float of grid classes
&[class*="col-"] {
float: none;
padding-left: 0;
padding-right: 0;
}
.form-control {
// Ensure that the input is always above the *appended* addon button for
// proper border colors.
position: relative;
z-index: 2;
// IE9 fubars the placeholder attribute in text inputs and the arrows on
// select elements in input groups. To fix it, we float the input. Details:
// https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855
float: left;
width: 100%;
margin-bottom: 0;
&:focus {
z-index: 3;
}
}
}
// Sizing options
//
// Remix the default form control sizing classes into new ones for easier
// manipulation.
.input-group-lg > .form-control,
.input-group-lg > .input-group-addon,
.input-group-lg > .input-group-btn > .btn {
.input-lg();
}
.input-group-sm > .form-control,
.input-group-sm > .input-group-addon,
.input-group-sm > .input-group-btn > .btn {
.input-sm();
}
// Display as table-cell
// -------------------------
.input-group-addon,
.input-group-btn,
.input-group .form-control {
display: table-cell;
&:not(:first-child):not(:last-child) {
border-radius: 0;
}
}
// Addon and addon wrapper for buttons
.input-group-addon,
.input-group-btn {
width: 1%;
white-space: nowrap;
vertical-align: middle; // Match the inputs
}
// Text input groups
// -------------------------
.input-group-addon {
padding: @padding-base-vertical @padding-base-horizontal;
font-size: @font-size-base;
font-weight: normal;
line-height: 1;
color: @input-color;
text-align: center;
background-color: @input-group-addon-bg;
border: 1px solid @input-group-addon-border-color;
border-radius: @input-border-radius;
// Sizing
&.input-sm {
padding: @padding-small-vertical @padding-small-horizontal;
font-size: @font-size-small;
border-radius: @input-border-radius-small;
}
&.input-lg {
padding: @padding-large-vertical @padding-large-horizontal;
font-size: @font-size-large;
border-radius: @input-border-radius-large;
}
// Nuke default margins from checkboxes and radios to vertically center within.
input[type="radio"],
input[type="checkbox"] {
margin-top: 0;
}
}
// Reset rounded corners
.input-group .form-control:first-child,
.input-group-addon:first-child,
.input-group-btn:first-child > .btn,
.input-group-btn:first-child > .btn-group > .btn,
.input-group-btn:first-child > .dropdown-toggle,
.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),
.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {
.border-right-radius(0);
}
.input-group-addon:first-child {
border-right: 0;
}
.input-group .form-control:last-child,
.input-group-addon:last-child,
.input-group-btn:last-child > .btn,
.input-group-btn:last-child > .btn-group > .btn,
.input-group-btn:last-child > .dropdown-toggle,
.input-group-btn:first-child > .btn:not(:first-child),
.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {
.border-left-radius(0);
}
.input-group-addon:last-child {
border-left: 0;
}
// Button input groups
// -------------------------
.input-group-btn {
position: relative;
// Jankily prevent input button groups from wrapping with `white-space` and
// `font-size` in combination with `inline-block` on buttons.
font-size: 0;
white-space: nowrap;
// Negative margin for spacing, position for bringing hovered/focused/actived
// element above the siblings.
> .btn {
position: relative;
+ .btn {
margin-left: -1px;
}
// Bring the "active" button to the front
&:hover,
&:focus,
&:active {
z-index: 2;
}
}
// Negative margin to only have a 1px border between the two
&:first-child {
> .btn,
> .btn-group {
margin-right: -1px;
}
}
&:last-child {
> .btn,
> .btn-group {
z-index: 2;
margin-left: -1px;
}
}
}
@@ -1,54 +0,0 @@
//
// Jumbotron
// --------------------------------------------------
.jumbotron {
padding-top: @jumbotron-padding;
padding-bottom: @jumbotron-padding;
margin-bottom: @jumbotron-padding;
color: @jumbotron-color;
background-color: @jumbotron-bg;
h1,
.h1 {
color: @jumbotron-heading-color;
}
p {
margin-bottom: (@jumbotron-padding / 2);
font-size: @jumbotron-font-size;
font-weight: 200;
}
> hr {
border-top-color: darken(@jumbotron-bg, 10%);
}
.container &,
.container-fluid & {
border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container
padding-left: (@grid-gutter-width / 2);
padding-right: (@grid-gutter-width / 2);
}
.container {
max-width: 100%;
}
@media screen and (min-width: @screen-sm-min) {
padding-top: (@jumbotron-padding * 1.6);
padding-bottom: (@jumbotron-padding * 1.6);
.container &,
.container-fluid & {
padding-left: (@jumbotron-padding * 2);
padding-right: (@jumbotron-padding * 2);
}
h1,
.h1 {
font-size: @jumbotron-heading-font-size;
}
}
}
@@ -1,64 +0,0 @@
//
// Labels
// --------------------------------------------------
.label {
display: inline;
padding: .2em .6em .3em;
font-size: 75%;
font-weight: bold;
line-height: 1;
color: @label-color;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25em;
// Add hover effects, but only for links
a& {
&:hover,
&:focus {
color: @label-link-hover-color;
text-decoration: none;
cursor: pointer;
}
}
// Empty labels collapse automatically (not available in IE8)
&:empty {
display: none;
}
// Quick fix for labels in buttons
.btn & {
position: relative;
top: -1px;
}
}
// Colors
// Contextual variations (linked labels get darker on :hover)
.label-default {
.label-variant(@label-default-bg);
}
.label-primary {
.label-variant(@label-primary-bg);
}
.label-success {
.label-variant(@label-success-bg);
}
.label-info {
.label-variant(@label-info-bg);
}
.label-warning {
.label-variant(@label-warning-bg);
}
.label-danger {
.label-variant(@label-danger-bg);
}
@@ -1,130 +0,0 @@
//
// List groups
// --------------------------------------------------
// Base class
//
// Easily usable on <ul>, <ol>, or <div>.
.list-group {
// No need to set list-style: none; since .list-group-item is block level
margin-bottom: 20px;
padding-left: 0; // reset padding because ul and ol
}
// Individual list items
//
// Use on `li`s or `div`s within the `.list-group` parent.
.list-group-item {
position: relative;
display: block;
padding: 10px 15px;
// Place the border on the list items and negative margin up for better styling
margin-bottom: -1px;
background-color: @list-group-bg;
border: 1px solid @list-group-border;
// Round the first and last items
&:first-child {
.border-top-radius(@list-group-border-radius);
}
&:last-child {
margin-bottom: 0;
.border-bottom-radius(@list-group-border-radius);
}
}
// Interactive list items
//
// Use anchor or button elements instead of `li`s or `div`s to create interactive items.
// Includes an extra `.active` modifier class for showing selected items.
a.list-group-item,
button.list-group-item {
color: @list-group-link-color;
.list-group-item-heading {
color: @list-group-link-heading-color;
}
// Hover state
&:hover,
&:focus {
text-decoration: none;
color: @list-group-link-hover-color;
background-color: @list-group-hover-bg;
}
}
button.list-group-item {
width: 100%;
text-align: left;
}
.list-group-item {
// Disabled state
&.disabled,
&.disabled:hover,
&.disabled:focus {
background-color: @list-group-disabled-bg;
color: @list-group-disabled-color;
cursor: @cursor-disabled;
// Force color to inherit for custom content
.list-group-item-heading {
color: inherit;
}
.list-group-item-text {
color: @list-group-disabled-text-color;
}
}
// Active class on item itself, not parent
&.active,
&.active:hover,
&.active:focus {
z-index: 2; // Place active items above their siblings for proper border styling
color: @list-group-active-color;
background-color: @list-group-active-bg;
border-color: @list-group-active-border;
// Force color to inherit for custom content
.list-group-item-heading,
.list-group-item-heading > small,
.list-group-item-heading > .small {
color: inherit;
}
.list-group-item-text {
color: @list-group-active-text-color;
}
}
}
// Contextual variants
//
// Add modifier classes to change text and background color on individual items.
// Organizationally, this must come after the `:hover` states.
.list-group-item-variant(success; @state-success-bg; @state-success-text);
.list-group-item-variant(info; @state-info-bg; @state-info-text);
.list-group-item-variant(warning; @state-warning-bg; @state-warning-text);
.list-group-item-variant(danger; @state-danger-bg; @state-danger-text);
// Custom content options
//
// Extra classes for creating well-formatted content within `.list-group-item`s.
.list-group-item-heading {
margin-top: 0;
margin-bottom: 5px;
}
.list-group-item-text {
margin-bottom: 0;
line-height: 1.3;
}
@@ -1,66 +0,0 @@
.media {
// Proper spacing between instances of .media
margin-top: 15px;
&:first-child {
margin-top: 0;
}
}
.media,
.media-body {
zoom: 1;
overflow: hidden;
}
.media-body {
width: 10000px;
}
.media-object {
display: block;
// Fix collapse in webkit from max-width: 100% and display: table-cell.
&.img-thumbnail {
max-width: none;
}
}
.media-right,
.media > .pull-right {
padding-left: 10px;
}
.media-left,
.media > .pull-left {
padding-right: 10px;
}
.media-left,
.media-right,
.media-body {
display: table-cell;
vertical-align: top;
}
.media-middle {
vertical-align: middle;
}
.media-bottom {
vertical-align: bottom;
}
// Reset margins on headings for tighter default spacing
.media-heading {
margin-top: 0;
margin-bottom: 5px;
}
// Media list variation
//
// Undo default ul/ol styles
.media-list {
padding-left: 0;
list-style: none;
}
@@ -1,40 +0,0 @@
// Mixins
// --------------------------------------------------
// Utilities
@import "mixins/hide-text.less";
@import "mixins/opacity.less";
@import "mixins/image.less";
@import "mixins/labels.less";
@import "mixins/reset-filter.less";
@import "mixins/resize.less";
@import "mixins/responsive-visibility.less";
@import "mixins/size.less";
@import "mixins/tab-focus.less";
@import "mixins/reset-text.less";
@import "mixins/text-emphasis.less";
@import "mixins/text-overflow.less";
@import "mixins/vendor-prefixes.less";
// Components
@import "mixins/alerts.less";
@import "mixins/buttons.less";
@import "mixins/panels.less";
@import "mixins/pagination.less";
@import "mixins/list-group.less";
@import "mixins/nav-divider.less";
@import "mixins/forms.less";
@import "mixins/progress-bar.less";
@import "mixins/table-row.less";
// Skins
@import "mixins/background-variant.less";
@import "mixins/border-radius.less";
@import "mixins/gradients.less";
// Layout
@import "mixins/clearfix.less";
@import "mixins/center-block.less";
@import "mixins/nav-vertical-align.less";
@import "mixins/grid-framework.less";
@import "mixins/grid.less";
@@ -1,14 +0,0 @@
// Alerts
.alert-variant(@background; @border; @text-color) {
background-color: @background;
border-color: @border;
color: @text-color;
hr {
border-top-color: darken(@border, 5%);
}
.alert-link {
color: darken(@text-color, 10%);
}
}
@@ -1,9 +0,0 @@
// Contextual backgrounds
.bg-variant(@color) {
background-color: @color;
a&:hover,
a&:focus {
background-color: darken(@color, 10%);
}
}
@@ -1,18 +0,0 @@
// Single side border-radius
.border-top-radius(@radius) {
border-top-right-radius: @radius;
border-top-left-radius: @radius;
}
.border-right-radius(@radius) {
border-bottom-right-radius: @radius;
border-top-right-radius: @radius;
}
.border-bottom-radius(@radius) {
border-bottom-right-radius: @radius;
border-bottom-left-radius: @radius;
}
.border-left-radius(@radius) {
border-bottom-left-radius: @radius;
border-top-left-radius: @radius;
}
@@ -1,65 +0,0 @@
// Button variants
//
// Easily pump out default styles, as well as :hover, :focus, :active,
// and disabled options for all buttons
.button-variant(@color; @background; @border) {
color: @color;
background-color: @background;
border-color: @border;
&:focus,
&.focus {
color: @color;
background-color: darken(@background, 10%);
border-color: darken(@border, 25%);
}
&:hover {
color: @color;
background-color: darken(@background, 10%);
border-color: darken(@border, 12%);
}
&:active,
&.active,
.open > .dropdown-toggle& {
color: @color;
background-color: darken(@background, 10%);
border-color: darken(@border, 12%);
&:hover,
&:focus,
&.focus {
color: @color;
background-color: darken(@background, 17%);
border-color: darken(@border, 25%);
}
}
&:active,
&.active,
.open > .dropdown-toggle& {
background-image: none;
}
&.disabled,
&[disabled],
fieldset[disabled] & {
&:hover,
&:focus,
&.focus {
background-color: @background;
border-color: @border;
}
}
.badge {
color: @background;
background-color: @color;
}
}
// Button sizes
.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {
padding: @padding-vertical @padding-horizontal;
font-size: @font-size;
line-height: @line-height;
border-radius: @border-radius;
}
@@ -1,7 +0,0 @@
// Center-align a block level element
.center-block() {
display: block;
margin-left: auto;
margin-right: auto;
}
@@ -1,22 +0,0 @@
// Clearfix
//
// For modern browsers
// 1. The space content is one way to avoid an Opera bug when the
// contenteditable attribute is included anywhere else in the document.
// Otherwise it causes space to appear at the top and bottom of elements
// that are clearfixed.
// 2. The use of `table` rather than `block` is only necessary if using
// `:before` to contain the top-margins of child elements.
//
// Source: http://nicolasgallagher.com/micro-clearfix-hack/
.clearfix() {
&:before,
&:after {
content: " "; // 1
display: table; // 2
}
&:after {
clear: both;
}
}
@@ -1,85 +0,0 @@
// Form validation states
//
// Used in forms.less to generate the form validation CSS for warnings, errors,
// and successes.
.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {
// Color the label and help text
.help-block,
.control-label,
.radio,
.checkbox,
.radio-inline,
.checkbox-inline,
&.radio label,
&.checkbox label,
&.radio-inline label,
&.checkbox-inline label {
color: @text-color;
}
// Set the border and box shadow on specific inputs to match
.form-control {
border-color: @border-color;
.box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
&:focus {
border-color: darken(@border-color, 10%);
@shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%);
.box-shadow(@shadow);
}
}
// Set validation states also for addons
.input-group-addon {
color: @text-color;
border-color: @border-color;
background-color: @background-color;
}
// Optional feedback icon
.form-control-feedback {
color: @text-color;
}
}
// Form control focus state
//
// Generate a customized focus state and for any input with the specified color,
// which defaults to the `@input-border-focus` variable.
//
// We highly encourage you to not customize the default value, but instead use
// this to tweak colors on an as-needed basis. This aesthetic change is based on
// WebKit's default styles, but applicable to a wider range of browsers. Its
// usability and accessibility should be taken into account with any change.
//
// Example usage: change the default blue border and shadow to white for better
// contrast against a dark gray background.
.form-control-focus(@color: @input-border-focus) {
@color-rgba: rgba(red(@color), green(@color), blue(@color), .6);
&:focus {
border-color: @color;
outline: 0;
.box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}");
}
}
// Form control sizing
//
// Relative text size, padding, and border-radii changes for form controls. For
// horizontal sizing, wrap controls in the predefined grid classes. `<select>`
// element gets special love because it's special, and that's a fact!
.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {
height: @input-height;
padding: @padding-vertical @padding-horizontal;
font-size: @font-size;
line-height: @line-height;
border-radius: @border-radius;
select& {
height: @input-height;
line-height: @input-height;
}
textarea&,
select[multiple]& {
height: auto;
}
}
@@ -1,59 +0,0 @@
// Gradients
#gradient {
// Horizontal gradient, from left to right
//
// Creates two color stops, start and end, by specifying a color and position for each color stop.
// Color stops are not available in IE9 and below.
.horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {
background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+
background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12
background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+
background-repeat: repeat-x;
filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@start-color),argb(@end-color))); // IE9 and down
}
// Vertical gradient, from top to bottom
//
// Creates two color stops, start and end, by specifying a color and position for each color stop.
// Color stops are not available in IE9 and below.
.vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {
background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+
background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12
background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+
background-repeat: repeat-x;
filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@start-color),argb(@end-color))); // IE9 and down
}
.directional(@start-color: #555; @end-color: #333; @deg: 45deg) {
background-repeat: repeat-x;
background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+
background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12
background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+
}
.horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {
background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);
background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);
background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);
background-repeat: no-repeat;
filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback
}
.vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {
background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);
background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);
background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);
background-repeat: no-repeat;
filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback
}
.radial(@inner-color: #555; @outer-color: #333) {
background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);
background-image: radial-gradient(circle, @inner-color, @outer-color);
background-repeat: no-repeat;
}
.striped(@color: rgba(255,255,255,.15); @angle: 45deg) {
background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);
background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);
}
}
@@ -1,91 +0,0 @@
// Framework grid generation
//
// Used only by Bootstrap to generate the correct number of grid classes given
// any value of `@grid-columns`.
.make-grid-columns() {
// Common styles for all sizes of grid columns, widths 1-12
.col(@index) { // initial
@item: ~".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}";
.col((@index + 1), @item);
}
.col(@index, @list) when (@index =< @grid-columns) { // general; "=<" isn't a typo
@item: ~".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}";
.col((@index + 1), ~"@{list}, @{item}");
}
.col(@index, @list) when (@index > @grid-columns) { // terminal
@{list} {
position: relative;
// Prevent columns from collapsing when empty
min-height: 1px;
// Inner gutter via padding
padding-left: ceil((@grid-gutter-width / 2));
padding-right: floor((@grid-gutter-width / 2));
}
}
.col(1); // kickstart it
}
.float-grid-columns(@class) {
.col(@index) { // initial
@item: ~".col-@{class}-@{index}";
.col((@index + 1), @item);
}
.col(@index, @list) when (@index =< @grid-columns) { // general
@item: ~".col-@{class}-@{index}";
.col((@index + 1), ~"@{list}, @{item}");
}
.col(@index, @list) when (@index > @grid-columns) { // terminal
@{list} {
float: left;
}
}
.col(1); // kickstart it
}
.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {
.col-@{class}-@{index} {
width: percentage((@index / @grid-columns));
}
}
.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {
.col-@{class}-push-@{index} {
left: percentage((@index / @grid-columns));
}
}
.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {
.col-@{class}-push-0 {
left: auto;
}
}
.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {
.col-@{class}-pull-@{index} {
right: percentage((@index / @grid-columns));
}
}
.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {
.col-@{class}-pull-0 {
right: auto;
}
}
.calc-grid-column(@index, @class, @type) when (@type = offset) {
.col-@{class}-offset-@{index} {
margin-left: percentage((@index / @grid-columns));
}
}
// Basic looping in LESS
.loop-grid-columns(@index, @class, @type) when (@index >= 0) {
.calc-grid-column(@index, @class, @type);
// next iteration
.loop-grid-columns((@index - 1), @class, @type);
}
// Create grid for specific class
.make-grid(@class) {
.float-grid-columns(@class);
.loop-grid-columns(@grid-columns, @class, width);
.loop-grid-columns(@grid-columns, @class, pull);
.loop-grid-columns(@grid-columns, @class, push);
.loop-grid-columns(@grid-columns, @class, offset);
}
@@ -1,122 +0,0 @@
// Grid system
//
// Generate semantic grid columns with these mixins.
// Centered container element
.container-fixed(@gutter: @grid-gutter-width) {
margin-right: auto;
margin-left: auto;
padding-left: floor((@gutter / 2));
padding-right: ceil((@gutter / 2));
&:extend(.clearfix all);
}
// Creates a wrapper for a series of columns
.make-row(@gutter: @grid-gutter-width) {
margin-left: ceil((@gutter / -2));
margin-right: floor((@gutter / -2));
&:extend(.clearfix all);
}
// Generate the extra small columns
.make-xs-column(@columns; @gutter: @grid-gutter-width) {
position: relative;
float: left;
width: percentage((@columns / @grid-columns));
min-height: 1px;
padding-left: (@gutter / 2);
padding-right: (@gutter / 2);
}
.make-xs-column-offset(@columns) {
margin-left: percentage((@columns / @grid-columns));
}
.make-xs-column-push(@columns) {
left: percentage((@columns / @grid-columns));
}
.make-xs-column-pull(@columns) {
right: percentage((@columns / @grid-columns));
}
// Generate the small columns
.make-sm-column(@columns; @gutter: @grid-gutter-width) {
position: relative;
min-height: 1px;
padding-left: (@gutter / 2);
padding-right: (@gutter / 2);
@media (min-width: @screen-sm-min) {
float: left;
width: percentage((@columns / @grid-columns));
}
}
.make-sm-column-offset(@columns) {
@media (min-width: @screen-sm-min) {
margin-left: percentage((@columns / @grid-columns));
}
}
.make-sm-column-push(@columns) {
@media (min-width: @screen-sm-min) {
left: percentage((@columns / @grid-columns));
}
}
.make-sm-column-pull(@columns) {
@media (min-width: @screen-sm-min) {
right: percentage((@columns / @grid-columns));
}
}
// Generate the medium columns
.make-md-column(@columns; @gutter: @grid-gutter-width) {
position: relative;
min-height: 1px;
padding-left: (@gutter / 2);
padding-right: (@gutter / 2);
@media (min-width: @screen-md-min) {
float: left;
width: percentage((@columns / @grid-columns));
}
}
.make-md-column-offset(@columns) {
@media (min-width: @screen-md-min) {
margin-left: percentage((@columns / @grid-columns));
}
}
.make-md-column-push(@columns) {
@media (min-width: @screen-md-min) {
left: percentage((@columns / @grid-columns));
}
}
.make-md-column-pull(@columns) {
@media (min-width: @screen-md-min) {
right: percentage((@columns / @grid-columns));
}
}
// Generate the large columns
.make-lg-column(@columns; @gutter: @grid-gutter-width) {
position: relative;
min-height: 1px;
padding-left: (@gutter / 2);
padding-right: (@gutter / 2);
@media (min-width: @screen-lg-min) {
float: left;
width: percentage((@columns / @grid-columns));
}
}
.make-lg-column-offset(@columns) {
@media (min-width: @screen-lg-min) {
margin-left: percentage((@columns / @grid-columns));
}
}
.make-lg-column-push(@columns) {
@media (min-width: @screen-lg-min) {
left: percentage((@columns / @grid-columns));
}
}
.make-lg-column-pull(@columns) {
@media (min-width: @screen-lg-min) {
right: percentage((@columns / @grid-columns));
}
}
@@ -1,21 +0,0 @@
// CSS image replacement
//
// Heads up! v3 launched with only `.hide-text()`, but per our pattern for
// mixins being reused as classes with the same name, this doesn't hold up. As
// of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`.
//
// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757
// Deprecated as of v3.0.1 (has been removed in v4)
.hide-text() {
font: ~"0/0" a;
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}
// New mixin to use as of v3.0.1
.text-hide() {
.hide-text();
}
@@ -1,33 +0,0 @@
// Image Mixins
// - Responsive image
// - Retina image
// Responsive image
//
// Keep images from scaling beyond the width of their parents.
.img-responsive(@display: block) {
display: @display;
max-width: 100%; // Part 1: Set a maximum relative to the parent
height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching
}
// Retina image
//
// Short retina mixin for setting background-image and -size. Note that the
// spelling of `min--moz-device-pixel-ratio` is intentional.
.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {
background-image: url("@{file-1x}");
@media
only screen and (-webkit-min-device-pixel-ratio: 2),
only screen and ( min--moz-device-pixel-ratio: 2),
only screen and ( -o-min-device-pixel-ratio: 2/1),
only screen and ( min-device-pixel-ratio: 2),
only screen and ( min-resolution: 192dpi),
only screen and ( min-resolution: 2dppx) {
background-image: url("@{file-2x}");
background-size: @width-1x @height-1x;
}
}
@@ -1,12 +0,0 @@
// Labels
.label-variant(@color) {
background-color: @color;
&[href] {
&:hover,
&:focus {
background-color: darken(@color, 10%);
}
}
}
@@ -1,30 +0,0 @@
// List Groups
.list-group-item-variant(@state; @background; @color) {
.list-group-item-@{state} {
color: @color;
background-color: @background;
a&,
button& {
color: @color;
.list-group-item-heading {
color: inherit;
}
&:hover,
&:focus {
color: @color;
background-color: darken(@background, 5%);
}
&.active,
&.active:hover,
&.active:focus {
color: #fff;
background-color: @color;
border-color: @color;
}
}
}
}
@@ -1,10 +0,0 @@
// Horizontal dividers
//
// Dividers (basically an hr) within dropdowns and nav lists
.nav-divider(@color: #e5e5e5) {
height: 1px;
margin: ((@line-height-computed / 2) - 1) 0;
overflow: hidden;
background-color: @color;
}
@@ -1,9 +0,0 @@
// Navbar vertical align
//
// Vertically center elements in the navbar.
// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.
.navbar-vertical-align(@element-height) {
margin-top: ((@navbar-height - @element-height) / 2);
margin-bottom: ((@navbar-height - @element-height) / 2);
}
@@ -1,8 +0,0 @@
// Opacity
.opacity(@opacity) {
opacity: @opacity;
// IE8 filter
@opacity-ie: (@opacity * 100);
filter: ~"alpha(opacity=@{opacity-ie})";
}
@@ -1,24 +0,0 @@
// Pagination
.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {
> li {
> a,
> span {
padding: @padding-vertical @padding-horizontal;
font-size: @font-size;
line-height: @line-height;
}
&:first-child {
> a,
> span {
.border-left-radius(@border-radius);
}
}
&:last-child {
> a,
> span {
.border-right-radius(@border-radius);
}
}
}
}
@@ -1,24 +0,0 @@
// Panels
.panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) {
border-color: @border;
& > .panel-heading {
color: @heading-text-color;
background-color: @heading-bg-color;
border-color: @heading-border;
+ .panel-collapse > .panel-body {
border-top-color: @border;
}
.badge {
color: @heading-bg-color;
background-color: @heading-text-color;
}
}
& > .panel-footer {
+ .panel-collapse > .panel-body {
border-bottom-color: @border;
}
}
}
@@ -1,10 +0,0 @@
// Progress bars
.progress-bar-variant(@color) {
background-color: @color;
// Deprecated parent class requirement as of v3.2.0
.progress-striped & {
#gradient > .striped();
}
}
@@ -1,8 +0,0 @@
// Reset filters for IE
//
// When you need to remove a gradient background, do not forget to use this to reset
// the IE filter for IE9 and below.
.reset-filter() {
filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)"));
}
@@ -1,18 +0,0 @@
.reset-text() {
font-family: @font-family-base;
// We deliberately do NOT reset font-size.
font-style: normal;
font-weight: normal;
letter-spacing: normal;
line-break: auto;
line-height: @line-height-base;
text-align: left; // Fallback for where `start` is not supported
text-align: start;
text-decoration: none;
text-shadow: none;
text-transform: none;
white-space: normal;
word-break: normal;
word-spacing: normal;
word-wrap: normal;
}
@@ -1,6 +0,0 @@
// Resize anything
.resizable(@direction) {
resize: @direction; // Options: horizontal, vertical, both
overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible`
}
@@ -1,15 +0,0 @@
// Responsive utilities
//
// More easily include all the states for responsive-utilities.less.
.responsive-visibility() {
display: block !important;
table& { display: table !important; }
tr& { display: table-row !important; }
th&,
td& { display: table-cell !important; }
}
.responsive-invisibility() {
display: none !important;
}
@@ -1,10 +0,0 @@
// Sizing shortcuts
.size(@width; @height) {
width: @width;
height: @height;
}
.square(@size) {
.size(@size; @size);
}
@@ -1,9 +0,0 @@
// WebKit-style focus
.tab-focus() {
// Default
outline: thin dotted;
// WebKit
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
@@ -1,28 +0,0 @@
// Tables
.table-row-variant(@state; @background) {
// Exact selectors below required to override `.table-striped` and prevent
// inheritance to nested tables.
.table > thead > tr,
.table > tbody > tr,
.table > tfoot > tr {
> td.@{state},
> th.@{state},
&.@{state} > td,
&.@{state} > th {
background-color: @background;
}
}
// Hover states for `.table-hover`
// Note: this is not available for cells or rows within `thead` or `tfoot`.
.table-hover > tbody > tr {
> td.@{state}:hover,
> th.@{state}:hover,
&.@{state}:hover > td,
&:hover > .@{state},
&.@{state}:hover > th {
background-color: darken(@background, 5%);
}
}
}
@@ -1,9 +0,0 @@
// Typography
.text-emphasis-variant(@color) {
color: @color;
a&:hover,
a&:focus {
color: darken(@color, 10%);
}
}
@@ -1,8 +0,0 @@
// Text overflow
// Requires inline-block or block for proper styling
.text-overflow() {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
@@ -1,227 +0,0 @@
// Vendor Prefixes
//
// All vendor mixins are deprecated as of v3.2.0 due to the introduction of
// Autoprefixer in our Gruntfile. They have been removed in v4.
// - Animations
// - Backface visibility
// - Box shadow
// - Box sizing
// - Content columns
// - Hyphens
// - Placeholder text
// - Transformations
// - Transitions
// - User Select
// Animations
.animation(@animation) {
-webkit-animation: @animation;
-o-animation: @animation;
animation: @animation;
}
.animation-name(@name) {
-webkit-animation-name: @name;
animation-name: @name;
}
.animation-duration(@duration) {
-webkit-animation-duration: @duration;
animation-duration: @duration;
}
.animation-timing-function(@timing-function) {
-webkit-animation-timing-function: @timing-function;
animation-timing-function: @timing-function;
}
.animation-delay(@delay) {
-webkit-animation-delay: @delay;
animation-delay: @delay;
}
.animation-iteration-count(@iteration-count) {
-webkit-animation-iteration-count: @iteration-count;
animation-iteration-count: @iteration-count;
}
.animation-direction(@direction) {
-webkit-animation-direction: @direction;
animation-direction: @direction;
}
.animation-fill-mode(@fill-mode) {
-webkit-animation-fill-mode: @fill-mode;
animation-fill-mode: @fill-mode;
}
// Backface visibility
// Prevent browsers from flickering when using CSS 3D transforms.
// Default value is `visible`, but can be changed to `hidden`
.backface-visibility(@visibility) {
-webkit-backface-visibility: @visibility;
-moz-backface-visibility: @visibility;
backface-visibility: @visibility;
}
// Drop shadows
//
// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's
// supported browsers that have box shadow capabilities now support it.
.box-shadow(@shadow) {
-webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1
box-shadow: @shadow;
}
// Box sizing
.box-sizing(@boxmodel) {
-webkit-box-sizing: @boxmodel;
-moz-box-sizing: @boxmodel;
box-sizing: @boxmodel;
}
// CSS3 Content Columns
.content-columns(@column-count; @column-gap: @grid-gutter-width) {
-webkit-column-count: @column-count;
-moz-column-count: @column-count;
column-count: @column-count;
-webkit-column-gap: @column-gap;
-moz-column-gap: @column-gap;
column-gap: @column-gap;
}
// Optional hyphenation
.hyphens(@mode: auto) {
word-wrap: break-word;
-webkit-hyphens: @mode;
-moz-hyphens: @mode;
-ms-hyphens: @mode; // IE10+
-o-hyphens: @mode;
hyphens: @mode;
}
// Placeholder text
.placeholder(@color: @input-color-placeholder) {
// Firefox
&::-moz-placeholder {
color: @color;
opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526
}
&:-ms-input-placeholder { color: @color; } // Internet Explorer 10+
&::-webkit-input-placeholder { color: @color; } // Safari and Chrome
}
// Transformations
.scale(@ratio) {
-webkit-transform: scale(@ratio);
-ms-transform: scale(@ratio); // IE9 only
-o-transform: scale(@ratio);
transform: scale(@ratio);
}
.scale(@ratioX; @ratioY) {
-webkit-transform: scale(@ratioX, @ratioY);
-ms-transform: scale(@ratioX, @ratioY); // IE9 only
-o-transform: scale(@ratioX, @ratioY);
transform: scale(@ratioX, @ratioY);
}
.scaleX(@ratio) {
-webkit-transform: scaleX(@ratio);
-ms-transform: scaleX(@ratio); // IE9 only
-o-transform: scaleX(@ratio);
transform: scaleX(@ratio);
}
.scaleY(@ratio) {
-webkit-transform: scaleY(@ratio);
-ms-transform: scaleY(@ratio); // IE9 only
-o-transform: scaleY(@ratio);
transform: scaleY(@ratio);
}
.skew(@x; @y) {
-webkit-transform: skewX(@x) skewY(@y);
-ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+
-o-transform: skewX(@x) skewY(@y);
transform: skewX(@x) skewY(@y);
}
.translate(@x; @y) {
-webkit-transform: translate(@x, @y);
-ms-transform: translate(@x, @y); // IE9 only
-o-transform: translate(@x, @y);
transform: translate(@x, @y);
}
.translate3d(@x; @y; @z) {
-webkit-transform: translate3d(@x, @y, @z);
transform: translate3d(@x, @y, @z);
}
.rotate(@degrees) {
-webkit-transform: rotate(@degrees);
-ms-transform: rotate(@degrees); // IE9 only
-o-transform: rotate(@degrees);
transform: rotate(@degrees);
}
.rotateX(@degrees) {
-webkit-transform: rotateX(@degrees);
-ms-transform: rotateX(@degrees); // IE9 only
-o-transform: rotateX(@degrees);
transform: rotateX(@degrees);
}
.rotateY(@degrees) {
-webkit-transform: rotateY(@degrees);
-ms-transform: rotateY(@degrees); // IE9 only
-o-transform: rotateY(@degrees);
transform: rotateY(@degrees);
}
.perspective(@perspective) {
-webkit-perspective: @perspective;
-moz-perspective: @perspective;
perspective: @perspective;
}
.perspective-origin(@perspective) {
-webkit-perspective-origin: @perspective;
-moz-perspective-origin: @perspective;
perspective-origin: @perspective;
}
.transform-origin(@origin) {
-webkit-transform-origin: @origin;
-moz-transform-origin: @origin;
-ms-transform-origin: @origin; // IE9 only
transform-origin: @origin;
}
// Transitions
.transition(@transition) {
-webkit-transition: @transition;
-o-transition: @transition;
transition: @transition;
}
.transition-property(@transition-property) {
-webkit-transition-property: @transition-property;
transition-property: @transition-property;
}
.transition-delay(@transition-delay) {
-webkit-transition-delay: @transition-delay;
transition-delay: @transition-delay;
}
.transition-duration(@transition-duration) {
-webkit-transition-duration: @transition-duration;
transition-duration: @transition-duration;
}
.transition-timing-function(@timing-function) {
-webkit-transition-timing-function: @timing-function;
transition-timing-function: @timing-function;
}
.transition-transform(@transition) {
-webkit-transition: -webkit-transform @transition;
-moz-transition: -moz-transform @transition;
-o-transition: -o-transform @transition;
transition: transform @transition;
}
// User select
// For selecting text on the page
.user-select(@select) {
-webkit-user-select: @select;
-moz-user-select: @select;
-ms-user-select: @select; // IE10+
user-select: @select;
}
@@ -1,150 +0,0 @@
//
// Modals
// --------------------------------------------------
// .modal-open - body class for killing the scroll
// .modal - container to scroll within
// .modal-dialog - positioning shell for the actual modal
// .modal-content - actual modal w/ bg and corners and shit
// Kill the scroll on the body
.modal-open {
overflow: hidden;
}
// Container that the modal scrolls within
.modal {
display: none;
overflow: hidden;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: @zindex-modal;
-webkit-overflow-scrolling: touch;
// Prevent Chrome on Windows from adding a focus outline. For details, see
// https://github.com/twbs/bootstrap/pull/10951.
outline: 0;
// When fading in the modal, animate it to slide down
&.fade .modal-dialog {
.translate(0, -25%);
.transition-transform(~"0.3s ease-out");
}
&.in .modal-dialog { .translate(0, 0) }
}
.modal-open .modal {
overflow-x: hidden;
overflow-y: auto;
}
// Shell div to position the modal with bottom padding
.modal-dialog {
position: relative;
width: auto;
margin: 10px;
}
// Actual modal
.modal-content {
position: relative;
background-color: @modal-content-bg;
border: 1px solid @modal-content-fallback-border-color; //old browsers fallback (ie8 etc)
border: 1px solid @modal-content-border-color;
border-radius: @border-radius-large;
.box-shadow(0 3px 9px rgba(0,0,0,.5));
background-clip: padding-box;
// Remove focus outline from opened modal
outline: 0;
}
// Modal background
.modal-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: @zindex-modal-background;
background-color: @modal-backdrop-bg;
// Fade for backdrop
&.fade { .opacity(0); }
&.in { .opacity(@modal-backdrop-opacity); }
}
// Modal header
// Top section of the modal w/ title and dismiss
.modal-header {
padding: @modal-title-padding;
border-bottom: 1px solid @modal-header-border-color;
&:extend(.clearfix all);
}
// Close icon
.modal-header .close {
margin-top: -2px;
}
// Title text within header
.modal-title {
margin: 0;
line-height: @modal-title-line-height;
}
// Modal body
// Where all modal content resides (sibling of .modal-header and .modal-footer)
.modal-body {
position: relative;
padding: @modal-inner-padding;
}
// Footer (for actions)
.modal-footer {
padding: @modal-inner-padding;
text-align: right; // right align buttons
border-top: 1px solid @modal-footer-border-color;
&:extend(.clearfix all); // clear it in case folks use .pull-* classes on buttons
// Properly space out buttons
.btn + .btn {
margin-left: 5px;
margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs
}
// but override that for button groups
.btn-group .btn + .btn {
margin-left: -1px;
}
// and override it for block buttons as well
.btn-block + .btn-block {
margin-left: 0;
}
}
// Measure scrollbar width for padding body during modal show/hide
.modal-scrollbar-measure {
position: absolute;
top: -9999px;
width: 50px;
height: 50px;
overflow: scroll;
}
// Scale up the modal
@media (min-width: @screen-sm-min) {
// Automatically set modal's width for larger viewports
.modal-dialog {
width: @modal-md;
margin: 30px auto;
}
.modal-content {
.box-shadow(0 5px 15px rgba(0,0,0,.5));
}
// Modal sizes
.modal-sm { width: @modal-sm; }
}
@media (min-width: @screen-md-min) {
.modal-lg { width: @modal-lg; }
}
@@ -1,660 +0,0 @@
//
// Navbars
// --------------------------------------------------
// Wrapper and base class
//
// Provide a static navbar from which we expand to create full-width, fixed, and
// other navbar variations.
.navbar {
position: relative;
min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode)
margin-bottom: @navbar-margin-bottom;
border: 1px solid transparent;
// Prevent floats from breaking the navbar
&:extend(.clearfix all);
@media (min-width: @grid-float-breakpoint) {
border-radius: @navbar-border-radius;
}
}
// Navbar heading
//
// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy
// styling of responsive aspects.
.navbar-header {
&:extend(.clearfix all);
@media (min-width: @grid-float-breakpoint) {
float: left;
}
}
// Navbar collapse (body)
//
// Group your navbar content into this for easy collapsing and expanding across
// various device sizes. By default, this content is collapsed when <768px, but
// will expand past that for a horizontal display.
//
// To start (on mobile devices) the navbar links, forms, and buttons are stacked
// vertically and include a `max-height` to overflow in case you have too much
// content for the user's viewport.
.navbar-collapse {
overflow-x: visible;
padding-right: @navbar-padding-horizontal;
padding-left: @navbar-padding-horizontal;
border-top: 1px solid transparent;
box-shadow: inset 0 1px 0 rgba(255,255,255,.1);
&:extend(.clearfix all);
-webkit-overflow-scrolling: touch;
&.in {
overflow-y: auto;
}
@media (min-width: @grid-float-breakpoint) {
width: auto;
border-top: 0;
box-shadow: none;
&.collapse {
display: block !important;
height: auto !important;
padding-bottom: 0; // Override default setting
overflow: visible !important;
}
&.in {
overflow-y: visible;
}
// Undo the collapse side padding for navbars with containers to ensure
// alignment of right-aligned contents.
.navbar-fixed-top &,
.navbar-static-top &,
.navbar-fixed-bottom & {
padding-left: 0;
padding-right: 0;
}
}
}
.navbar-fixed-top,
.navbar-fixed-bottom {
.navbar-collapse {
max-height: @navbar-collapse-max-height;
@media (max-device-width: @screen-xs-min) and (orientation: landscape) {
max-height: 200px;
}
}
}
// Both navbar header and collapse
//
// When a container is present, change the behavior of the header and collapse.
.container,
.container-fluid {
> .navbar-header,
> .navbar-collapse {
margin-right: -@navbar-padding-horizontal;
margin-left: -@navbar-padding-horizontal;
@media (min-width: @grid-float-breakpoint) {
margin-right: 0;
margin-left: 0;
}
}
}
//
// Navbar alignment options
//
// Display the navbar across the entirety of the page or fixed it to the top or
// bottom of the page.
// Static top (unfixed, but 100% wide) navbar
.navbar-static-top {
z-index: @zindex-navbar;
border-width: 0 0 1px;
@media (min-width: @grid-float-breakpoint) {
border-radius: 0;
}
}
// Fix the top/bottom navbars when screen real estate supports it
.navbar-fixed-top,
.navbar-fixed-bottom {
position: fixed;
right: 0;
left: 0;
z-index: @zindex-navbar-fixed;
// Undo the rounded corners
@media (min-width: @grid-float-breakpoint) {
border-radius: 0;
}
}
.navbar-fixed-top {
top: 0;
border-width: 0 0 1px;
}
.navbar-fixed-bottom {
bottom: 0;
margin-bottom: 0; // override .navbar defaults
border-width: 1px 0 0;
}
// Brand/project name
.navbar-brand {
float: left;
padding: @navbar-padding-vertical @navbar-padding-horizontal;
font-size: @font-size-large;
line-height: @line-height-computed;
height: @navbar-height;
&:hover,
&:focus {
text-decoration: none;
}
> img {
display: block;
}
@media (min-width: @grid-float-breakpoint) {
.navbar > .container &,
.navbar > .container-fluid & {
margin-left: -@navbar-padding-horizontal;
}
}
}
// Navbar toggle
//
// Custom button for toggling the `.navbar-collapse`, powered by the collapse
// JavaScript plugin.
.navbar-toggle {
position: relative;
float: right;
margin-right: @navbar-padding-horizontal;
padding: 9px 10px;
.navbar-vertical-align(34px);
background-color: transparent;
background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214
border: 1px solid transparent;
border-radius: @border-radius-base;
// We remove the `outline` here, but later compensate by attaching `:hover`
// styles to `:focus`.
&:focus {
outline: 0;
}
// Bars
.icon-bar {
display: block;
width: 22px;
height: 2px;
border-radius: 1px;
}
.icon-bar + .icon-bar {
margin-top: 4px;
}
@media (min-width: @grid-float-breakpoint) {
display: none;
}
}
// Navbar nav links
//
// Builds on top of the `.nav` components with its own modifier class to make
// the nav the full height of the horizontal nav (above 768px).
.navbar-nav {
margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal;
> li > a {
padding-top: 10px;
padding-bottom: 10px;
line-height: @line-height-computed;
}
@media (max-width: @grid-float-breakpoint-max) {
// Dropdowns get custom display when collapsed
.open .dropdown-menu {
position: static;
float: none;
width: auto;
margin-top: 0;
background-color: transparent;
border: 0;
box-shadow: none;
> li > a,
.dropdown-header {
padding: 5px 15px 5px 25px;
}
> li > a {
line-height: @line-height-computed;
&:hover,
&:focus {
background-image: none;
}
}
}
}
// Uncollapse the nav
@media (min-width: @grid-float-breakpoint) {
float: left;
margin: 0;
> li {
float: left;
> a {
padding-top: @navbar-padding-vertical;
padding-bottom: @navbar-padding-vertical;
}
}
}
}
// Navbar form
//
// Extension of the `.form-inline` with some extra flavor for optimum display in
// our navbars.
.navbar-form {
margin-left: -@navbar-padding-horizontal;
margin-right: -@navbar-padding-horizontal;
padding: 10px @navbar-padding-horizontal;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
@shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);
.box-shadow(@shadow);
// Mixin behavior for optimum display
.form-inline();
.form-group {
@media (max-width: @grid-float-breakpoint-max) {
margin-bottom: 5px;
&:last-child {
margin-bottom: 0;
}
}
}
// Vertically center in expanded, horizontal navbar
.navbar-vertical-align(@input-height-base);
// Undo 100% width for pull classes
@media (min-width: @grid-float-breakpoint) {
width: auto;
border: 0;
margin-left: 0;
margin-right: 0;
padding-top: 0;
padding-bottom: 0;
.box-shadow(none);
}
}
// Dropdown menus
// Menu position and menu carets
.navbar-nav > li > .dropdown-menu {
margin-top: 0;
.border-top-radius(0);
}
// Menu position and menu caret support for dropups via extra dropup class
.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {
margin-bottom: 0;
.border-top-radius(@navbar-border-radius);
.border-bottom-radius(0);
}
// Buttons in navbars
//
// Vertically center a button within a navbar (when *not* in a form).
.navbar-btn {
.navbar-vertical-align(@input-height-base);
&.btn-sm {
.navbar-vertical-align(@input-height-small);
}
&.btn-xs {
.navbar-vertical-align(22);
}
}
// Text in navbars
//
// Add a class to make any element properly align itself vertically within the navbars.
.navbar-text {
.navbar-vertical-align(@line-height-computed);
@media (min-width: @grid-float-breakpoint) {
float: left;
margin-left: @navbar-padding-horizontal;
margin-right: @navbar-padding-horizontal;
}
}
// Component alignment
//
// Repurpose the pull utilities as their own navbar utilities to avoid specificity
// issues with parents and chaining. Only do this when the navbar is uncollapsed
// though so that navbar contents properly stack and align in mobile.
//
// Declared after the navbar components to ensure more specificity on the margins.
@media (min-width: @grid-float-breakpoint) {
.navbar-left { .pull-left(); }
.navbar-right {
.pull-right();
margin-right: -@navbar-padding-horizontal;
~ .navbar-right {
margin-right: 0;
}
}
}
// Alternate navbars
// --------------------------------------------------
// Default navbar
.navbar-default {
background-color: @navbar-default-bg;
border-color: @navbar-default-border;
.navbar-brand {
color: @navbar-default-brand-color;
&:hover,
&:focus {
color: @navbar-default-brand-hover-color;
background-color: @navbar-default-brand-hover-bg;
}
}
.navbar-text {
color: @navbar-default-color;
}
.navbar-nav {
> li > a {
color: @navbar-default-link-color;
&:hover,
&:focus {
color: @navbar-default-link-hover-color;
background-color: @navbar-default-link-hover-bg;
}
}
> .active > a {
&,
&:hover,
&:focus {
color: @navbar-default-link-active-color;
background-color: @navbar-default-link-active-bg;
}
}
> .disabled > a {
&,
&:hover,
&:focus {
color: @navbar-default-link-disabled-color;
background-color: @navbar-default-link-disabled-bg;
}
}
}
.navbar-toggle {
border-color: @navbar-default-toggle-border-color;
&:hover,
&:focus {
background-color: @navbar-default-toggle-hover-bg;
}
.icon-bar {
background-color: @navbar-default-toggle-icon-bar-bg;
}
}
.navbar-collapse,
.navbar-form {
border-color: @navbar-default-border;
}
// Dropdown menu items
.navbar-nav {
// Remove background color from open dropdown
> .open > a {
&,
&:hover,
&:focus {
background-color: @navbar-default-link-active-bg;
color: @navbar-default-link-active-color;
}
}
@media (max-width: @grid-float-breakpoint-max) {
// Dropdowns get custom display when collapsed
.open .dropdown-menu {
> li > a {
color: @navbar-default-link-color;
&:hover,
&:focus {
color: @navbar-default-link-hover-color;
background-color: @navbar-default-link-hover-bg;
}
}
> .active > a {
&,
&:hover,
&:focus {
color: @navbar-default-link-active-color;
background-color: @navbar-default-link-active-bg;
}
}
> .disabled > a {
&,
&:hover,
&:focus {
color: @navbar-default-link-disabled-color;
background-color: @navbar-default-link-disabled-bg;
}
}
}
}
}
// Links in navbars
//
// Add a class to ensure links outside the navbar nav are colored correctly.
.navbar-link {
color: @navbar-default-link-color;
&:hover {
color: @navbar-default-link-hover-color;
}
}
.btn-link {
color: @navbar-default-link-color;
&:hover,
&:focus {
color: @navbar-default-link-hover-color;
}
&[disabled],
fieldset[disabled] & {
&:hover,
&:focus {
color: @navbar-default-link-disabled-color;
}
}
}
}
// Inverse navbar
.navbar-inverse {
background-color: @navbar-inverse-bg;
border-color: @navbar-inverse-border;
.navbar-brand {
color: @navbar-inverse-brand-color;
&:hover,
&:focus {
color: @navbar-inverse-brand-hover-color;
background-color: @navbar-inverse-brand-hover-bg;
}
}
.navbar-text {
color: @navbar-inverse-color;
}
.navbar-nav {
> li > a {
color: @navbar-inverse-link-color;
&:hover,
&:focus {
color: @navbar-inverse-link-hover-color;
background-color: @navbar-inverse-link-hover-bg;
}
}
> .active > a {
&,
&:hover,
&:focus {
color: @navbar-inverse-link-active-color;
background-color: @navbar-inverse-link-active-bg;
}
}
> .disabled > a {
&,
&:hover,
&:focus {
color: @navbar-inverse-link-disabled-color;
background-color: @navbar-inverse-link-disabled-bg;
}
}
}
// Darken the responsive nav toggle
.navbar-toggle {
border-color: @navbar-inverse-toggle-border-color;
&:hover,
&:focus {
background-color: @navbar-inverse-toggle-hover-bg;
}
.icon-bar {
background-color: @navbar-inverse-toggle-icon-bar-bg;
}
}
.navbar-collapse,
.navbar-form {
border-color: darken(@navbar-inverse-bg, 7%);
}
// Dropdowns
.navbar-nav {
> .open > a {
&,
&:hover,
&:focus {
background-color: @navbar-inverse-link-active-bg;
color: @navbar-inverse-link-active-color;
}
}
@media (max-width: @grid-float-breakpoint-max) {
// Dropdowns get custom display
.open .dropdown-menu {
> .dropdown-header {
border-color: @navbar-inverse-border;
}
.divider {
background-color: @navbar-inverse-border;
}
> li > a {
color: @navbar-inverse-link-color;
&:hover,
&:focus {
color: @navbar-inverse-link-hover-color;
background-color: @navbar-inverse-link-hover-bg;
}
}
> .active > a {
&,
&:hover,
&:focus {
color: @navbar-inverse-link-active-color;
background-color: @navbar-inverse-link-active-bg;
}
}
> .disabled > a {
&,
&:hover,
&:focus {
color: @navbar-inverse-link-disabled-color;
background-color: @navbar-inverse-link-disabled-bg;
}
}
}
}
}
.navbar-link {
color: @navbar-inverse-link-color;
&:hover {
color: @navbar-inverse-link-hover-color;
}
}
.btn-link {
color: @navbar-inverse-link-color;
&:hover,
&:focus {
color: @navbar-inverse-link-hover-color;
}
&[disabled],
fieldset[disabled] & {
&:hover,
&:focus {
color: @navbar-inverse-link-disabled-color;
}
}
}
}
@@ -1,242 +0,0 @@
//
// Navs
// --------------------------------------------------
// Base class
// --------------------------------------------------
.nav {
margin-bottom: 0;
padding-left: 0; // Override default ul/ol
list-style: none;
&:extend(.clearfix all);
> li {
position: relative;
display: block;
> a {
position: relative;
display: block;
padding: @nav-link-padding;
&:hover,
&:focus {
text-decoration: none;
background-color: @nav-link-hover-bg;
}
}
// Disabled state sets text to gray and nukes hover/tab effects
&.disabled > a {
color: @nav-disabled-link-color;
&:hover,
&:focus {
color: @nav-disabled-link-hover-color;
text-decoration: none;
background-color: transparent;
cursor: @cursor-disabled;
}
}
}
// Open dropdowns
.open > a {
&,
&:hover,
&:focus {
background-color: @nav-link-hover-bg;
border-color: @link-color;
}
}
// Nav dividers (deprecated with v3.0.1)
//
// This should have been removed in v3 with the dropping of `.nav-list`, but
// we missed it. We don't currently support this anywhere, but in the interest
// of maintaining backward compatibility in case you use it, it's deprecated.
.nav-divider {
.nav-divider();
}
// Prevent IE8 from misplacing imgs
//
// See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989
> li > a > img {
max-width: none;
}
}
// Tabs
// -------------------------
// Give the tabs something to sit on
.nav-tabs {
border-bottom: 1px solid @nav-tabs-border-color;
> li {
float: left;
// Make the list-items overlay the bottom border
margin-bottom: -1px;
// Actual tabs (as links)
> a {
margin-right: 2px;
line-height: @line-height-base;
border: 1px solid transparent;
border-radius: @border-radius-base @border-radius-base 0 0;
&:hover {
border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color;
}
}
// Active state, and its :hover to override normal :hover
&.active > a {
&,
&:hover,
&:focus {
color: @nav-tabs-active-link-hover-color;
background-color: @nav-tabs-active-link-hover-bg;
border: 1px solid @nav-tabs-active-link-hover-border-color;
border-bottom-color: transparent;
cursor: default;
}
}
}
// pulling this in mainly for less shorthand
&.nav-justified {
.nav-justified();
.nav-tabs-justified();
}
}
// Pills
// -------------------------
.nav-pills {
> li {
float: left;
// Links rendered as pills
> a {
border-radius: @nav-pills-border-radius;
}
+ li {
margin-left: 2px;
}
// Active state
&.active > a {
&,
&:hover,
&:focus {
color: @nav-pills-active-link-hover-color;
background-color: @nav-pills-active-link-hover-bg;
}
}
}
}
// Stacked pills
.nav-stacked {
> li {
float: none;
+ li {
margin-top: 2px;
margin-left: 0; // no need for this gap between nav items
}
}
}
// Nav variations
// --------------------------------------------------
// Justified nav links
// -------------------------
.nav-justified {
width: 100%;
> li {
float: none;
> a {
text-align: center;
margin-bottom: 5px;
}
}
> .dropdown .dropdown-menu {
top: auto;
left: auto;
}
@media (min-width: @screen-sm-min) {
> li {
display: table-cell;
width: 1%;
> a {
margin-bottom: 0;
}
}
}
}
// Move borders to anchors instead of bottom of list
//
// Mixin for adding on top the shared `.nav-justified` styles for our tabs
.nav-tabs-justified {
border-bottom: 0;
> li > a {
// Override margin from .nav-tabs
margin-right: 0;
border-radius: @border-radius-base;
}
> .active > a,
> .active > a:hover,
> .active > a:focus {
border: 1px solid @nav-tabs-justified-link-border-color;
}
@media (min-width: @screen-sm-min) {
> li > a {
border-bottom: 1px solid @nav-tabs-justified-link-border-color;
border-radius: @border-radius-base @border-radius-base 0 0;
}
> .active > a,
> .active > a:hover,
> .active > a:focus {
border-bottom-color: @nav-tabs-justified-active-link-border-color;
}
}
}
// Tabbable tabs
// -------------------------
// Hide tabbable panes to start, show them when `.active`
.tab-content {
> .tab-pane {
display: none;
}
> .active {
display: block;
}
}
// Dropdowns
// -------------------------
// Specific dropdowns
.nav-tabs .dropdown-menu {
// make dropdown border overlap tab border
margin-top: -1px;
// Remove the top rounded corners here since there is a hard edge above the menu
.border-top-radius(0);
}
@@ -1,424 +0,0 @@
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
//
// 1. Set default font family to sans-serif.
// 2. Prevent iOS and IE text size adjust after device orientation change,
// without disabling user zoom.
//
html {
font-family: sans-serif; // 1
-ms-text-size-adjust: 100%; // 2
-webkit-text-size-adjust: 100%; // 2
}
//
// Remove default margin.
//
body {
margin: 0;
}
// HTML5 display definitions
// ==========================================================================
//
// Correct `block` display not defined for any HTML5 element in IE 8/9.
// Correct `block` display not defined for `details` or `summary` in IE 10/11
// and Firefox.
// Correct `block` display not defined for `main` in IE 11.
//
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
display: block;
}
//
// 1. Correct `inline-block` display not defined in IE 8/9.
// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
//
audio,
canvas,
progress,
video {
display: inline-block; // 1
vertical-align: baseline; // 2
}
//
// Prevent modern browsers from displaying `audio` without controls.
// Remove excess height in iOS 5 devices.
//
audio:not([controls]) {
display: none;
height: 0;
}
//
// Address `[hidden]` styling not present in IE 8/9/10.
// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.
//
[hidden],
template {
display: none;
}
// Links
// ==========================================================================
//
// Remove the gray background color from active links in IE 10.
//
a {
background-color: transparent;
}
//
// Improve readability of focused elements when they are also in an
// active/hover state.
//
a:active,
a:hover {
outline: 0;
}
// Text-level semantics
// ==========================================================================
//
// Address styling not present in IE 8/9/10/11, Safari, and Chrome.
//
abbr[title] {
border-bottom: 1px dotted;
}
//
// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
//
b,
strong {
font-weight: bold;
}
//
// Address styling not present in Safari and Chrome.
//
dfn {
font-style: italic;
}
//
// Address variable `h1` font-size and margin within `section` and `article`
// contexts in Firefox 4+, Safari, and Chrome.
//
h1 {
font-size: 2em;
margin: 0.67em 0;
}
//
// Address styling not present in IE 8/9.
//
mark {
background: #ff0;
color: #000;
}
//
// Address inconsistent and variable font size in all browsers.
//
small {
font-size: 80%;
}
//
// Prevent `sub` and `sup` affecting `line-height` in all browsers.
//
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
// Embedded content
// ==========================================================================
//
// Remove border when inside `a` element in IE 8/9/10.
//
img {
border: 0;
}
//
// Correct overflow not hidden in IE 9/10/11.
//
svg:not(:root) {
overflow: hidden;
}
// Grouping content
// ==========================================================================
//
// Address margin not present in IE 8/9 and Safari.
//
figure {
margin: 1em 40px;
}
//
// Address differences between Firefox and other browsers.
//
hr {
box-sizing: content-box;
height: 0;
}
//
// Contain overflow in all browsers.
//
pre {
overflow: auto;
}
//
// Address odd `em`-unit font size rendering in all browsers.
//
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
// Forms
// ==========================================================================
//
// Known limitation: by default, Chrome and Safari on OS X allow very limited
// styling of `select`, unless a `border` property is set.
//
//
// 1. Correct color not being inherited.
// Known issue: affects color of disabled elements.
// 2. Correct font properties not being inherited.
// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
//
button,
input,
optgroup,
select,
textarea {
color: inherit; // 1
font: inherit; // 2
margin: 0; // 3
}
//
// Address `overflow` set to `hidden` in IE 8/9/10/11.
//
button {
overflow: visible;
}
//
// Address inconsistent `text-transform` inheritance for `button` and `select`.
// All other form control elements do not inherit `text-transform` values.
// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
// Correct `select` style inheritance in Firefox.
//
button,
select {
text-transform: none;
}
//
// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
// and `video` controls.
// 2. Correct inability to style clickable `input` types in iOS.
// 3. Improve usability and consistency of cursor style between image-type
// `input` and others.
//
button,
html input[type="button"], // 1
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; // 2
cursor: pointer; // 3
}
//
// Re-set default cursor for disabled elements.
//
button[disabled],
html input[disabled] {
cursor: default;
}
//
// Remove inner padding and border in Firefox 4+.
//
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
//
// Address Firefox 4+ setting `line-height` on `input` using `!important` in
// the UA stylesheet.
//
input {
line-height: normal;
}
//
// It's recommended that you don't attempt to style these elements.
// Firefox's implementation doesn't respect box-sizing, padding, or width.
//
// 1. Address box sizing set to `content-box` in IE 8/9/10.
// 2. Remove excess padding in IE 8/9/10.
//
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; // 1
padding: 0; // 2
}
//
// Fix the cursor style for Chrome's increment/decrement buttons. For certain
// `font-size` values of the `input`, it causes the cursor style of the
// decrement button to change from `default` to `text`.
//
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
//
// 1. Address `appearance` set to `searchfield` in Safari and Chrome.
// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.
//
input[type="search"] {
-webkit-appearance: textfield; // 1
box-sizing: content-box; //2
}
//
// Remove inner padding and search cancel button in Safari and Chrome on OS X.
// Safari (but not Chrome) clips the cancel button when the search input has
// padding (and `textfield` appearance).
//
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
//
// Define consistent border, margin, and padding.
//
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
//
// 1. Correct `color` not being inherited in IE 8/9/10/11.
// 2. Remove padding so people aren't caught out if they zero out fieldsets.
//
legend {
border: 0; // 1
padding: 0; // 2
}
//
// Remove default vertical scrollbar in IE 8/9/10/11.
//
textarea {
overflow: auto;
}
//
// Don't inherit the `font-weight` (applied by a rule above).
// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
//
optgroup {
font-weight: bold;
}
// Tables
// ==========================================================================
//
// Remove most spacing between table cells.
//
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}
@@ -1,54 +0,0 @@
//
// Pager pagination
// --------------------------------------------------
.pager {
padding-left: 0;
margin: @line-height-computed 0;
list-style: none;
text-align: center;
&:extend(.clearfix all);
li {
display: inline;
> a,
> span {
display: inline-block;
padding: 5px 14px;
background-color: @pager-bg;
border: 1px solid @pager-border;
border-radius: @pager-border-radius;
}
> a:hover,
> a:focus {
text-decoration: none;
background-color: @pager-hover-bg;
}
}
.next {
> a,
> span {
float: right;
}
}
.previous {
> a,
> span {
float: left;
}
}
.disabled {
> a,
> a:hover,
> a:focus,
> span {
color: @pager-disabled-color;
background-color: @pager-bg;
cursor: @cursor-disabled;
}
}
}
@@ -1,89 +0,0 @@
//
// Pagination (multiple pages)
// --------------------------------------------------
.pagination {
display: inline-block;
padding-left: 0;
margin: @line-height-computed 0;
border-radius: @border-radius-base;
> li {
display: inline; // Remove list-style and block-level defaults
> a,
> span {
position: relative;
float: left; // Collapse white-space
padding: @padding-base-vertical @padding-base-horizontal;
line-height: @line-height-base;
text-decoration: none;
color: @pagination-color;
background-color: @pagination-bg;
border: 1px solid @pagination-border;
margin-left: -1px;
}
&:first-child {
> a,
> span {
margin-left: 0;
.border-left-radius(@border-radius-base);
}
}
&:last-child {
> a,
> span {
.border-right-radius(@border-radius-base);
}
}
}
> li > a,
> li > span {
&:hover,
&:focus {
z-index: 2;
color: @pagination-hover-color;
background-color: @pagination-hover-bg;
border-color: @pagination-hover-border;
}
}
> .active > a,
> .active > span {
&,
&:hover,
&:focus {
z-index: 3;
color: @pagination-active-color;
background-color: @pagination-active-bg;
border-color: @pagination-active-border;
cursor: default;
}
}
> .disabled {
> span,
> span:hover,
> span:focus,
> a,
> a:hover,
> a:focus {
color: @pagination-disabled-color;
background-color: @pagination-disabled-bg;
border-color: @pagination-disabled-border;
cursor: @cursor-disabled;
}
}
}
// Sizing
// --------------------------------------------------
// Large
.pagination-lg {
.pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);
}
// Small
.pagination-sm {
.pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);
}
@@ -1,271 +0,0 @@
//
// Panels
// --------------------------------------------------
// Base class
.panel {
margin-bottom: @line-height-computed;
background-color: @panel-bg;
border: 1px solid transparent;
border-radius: @panel-border-radius;
.box-shadow(0 1px 1px rgba(0,0,0,.05));
}
// Panel contents
.panel-body {
padding: @panel-body-padding;
&:extend(.clearfix all);
}
// Optional heading
.panel-heading {
padding: @panel-heading-padding;
border-bottom: 1px solid transparent;
.border-top-radius((@panel-border-radius - 1));
> .dropdown .dropdown-toggle {
color: inherit;
}
}
// Within heading, strip any `h*` tag of its default margins for spacing.
.panel-title {
margin-top: 0;
margin-bottom: 0;
font-size: ceil((@font-size-base * 1.125));
color: inherit;
> a,
> small,
> .small,
> small > a,
> .small > a {
color: inherit;
}
}
// Optional footer (stays gray in every modifier class)
.panel-footer {
padding: @panel-footer-padding;
background-color: @panel-footer-bg;
border-top: 1px solid @panel-inner-border;
.border-bottom-radius((@panel-border-radius - 1));
}
// List groups in panels
//
// By default, space out list group content from panel headings to account for
// any kind of custom content between the two.
.panel {
> .list-group,
> .panel-collapse > .list-group {
margin-bottom: 0;
.list-group-item {
border-width: 1px 0;
border-radius: 0;
}
// Add border top radius for first one
&:first-child {
.list-group-item:first-child {
border-top: 0;
.border-top-radius((@panel-border-radius - 1));
}
}
// Add border bottom radius for last one
&:last-child {
.list-group-item:last-child {
border-bottom: 0;
.border-bottom-radius((@panel-border-radius - 1));
}
}
}
> .panel-heading + .panel-collapse > .list-group {
.list-group-item:first-child {
.border-top-radius(0);
}
}
}
// Collapse space between when there's no additional content.
.panel-heading + .list-group {
.list-group-item:first-child {
border-top-width: 0;
}
}
.list-group + .panel-footer {
border-top-width: 0;
}
// Tables in panels
//
// Place a non-bordered `.table` within a panel (not within a `.panel-body`) and
// watch it go full width.
.panel {
> .table,
> .table-responsive > .table,
> .panel-collapse > .table {
margin-bottom: 0;
caption {
padding-left: @panel-body-padding;
padding-right: @panel-body-padding;
}
}
// Add border top radius for first one
> .table:first-child,
> .table-responsive:first-child > .table:first-child {
.border-top-radius((@panel-border-radius - 1));
> thead:first-child,
> tbody:first-child {
> tr:first-child {
border-top-left-radius: (@panel-border-radius - 1);
border-top-right-radius: (@panel-border-radius - 1);
td:first-child,
th:first-child {
border-top-left-radius: (@panel-border-radius - 1);
}
td:last-child,
th:last-child {
border-top-right-radius: (@panel-border-radius - 1);
}
}
}
}
// Add border bottom radius for last one
> .table:last-child,
> .table-responsive:last-child > .table:last-child {
.border-bottom-radius((@panel-border-radius - 1));
> tbody:last-child,
> tfoot:last-child {
> tr:last-child {
border-bottom-left-radius: (@panel-border-radius - 1);
border-bottom-right-radius: (@panel-border-radius - 1);
td:first-child,
th:first-child {
border-bottom-left-radius: (@panel-border-radius - 1);
}
td:last-child,
th:last-child {
border-bottom-right-radius: (@panel-border-radius - 1);
}
}
}
}
> .panel-body + .table,
> .panel-body + .table-responsive,
> .table + .panel-body,
> .table-responsive + .panel-body {
border-top: 1px solid @table-border-color;
}
> .table > tbody:first-child > tr:first-child th,
> .table > tbody:first-child > tr:first-child td {
border-top: 0;
}
> .table-bordered,
> .table-responsive > .table-bordered {
border: 0;
> thead,
> tbody,
> tfoot {
> tr {
> th:first-child,
> td:first-child {
border-left: 0;
}
> th:last-child,
> td:last-child {
border-right: 0;
}
}
}
> thead,
> tbody {
> tr:first-child {
> td,
> th {
border-bottom: 0;
}
}
}
> tbody,
> tfoot {
> tr:last-child {
> td,
> th {
border-bottom: 0;
}
}
}
}
> .table-responsive {
border: 0;
margin-bottom: 0;
}
}
// Collapsable panels (aka, accordion)
//
// Wrap a series of panels in `.panel-group` to turn them into an accordion with
// the help of our collapse JavaScript plugin.
.panel-group {
margin-bottom: @line-height-computed;
// Tighten up margin so it's only between panels
.panel {
margin-bottom: 0;
border-radius: @panel-border-radius;
+ .panel {
margin-top: 5px;
}
}
.panel-heading {
border-bottom: 0;
+ .panel-collapse > .panel-body,
+ .panel-collapse > .list-group {
border-top: 1px solid @panel-inner-border;
}
}
.panel-footer {
border-top: 0;
+ .panel-collapse .panel-body {
border-bottom: 1px solid @panel-inner-border;
}
}
}
// Contextual variations
.panel-default {
.panel-variant(@panel-default-border; @panel-default-text; @panel-default-heading-bg; @panel-default-border);
}
.panel-primary {
.panel-variant(@panel-primary-border; @panel-primary-text; @panel-primary-heading-bg; @panel-primary-border);
}
.panel-success {
.panel-variant(@panel-success-border; @panel-success-text; @panel-success-heading-bg; @panel-success-border);
}
.panel-info {
.panel-variant(@panel-info-border; @panel-info-text; @panel-info-heading-bg; @panel-info-border);
}
.panel-warning {
.panel-variant(@panel-warning-border; @panel-warning-text; @panel-warning-heading-bg; @panel-warning-border);
}
.panel-danger {
.panel-variant(@panel-danger-border; @panel-danger-text; @panel-danger-heading-bg; @panel-danger-border);
}
@@ -1,131 +0,0 @@
//
// Popovers
// --------------------------------------------------
.popover {
position: absolute;
top: 0;
left: 0;
z-index: @zindex-popover;
display: none;
max-width: @popover-max-width;
padding: 1px;
// Our parent element can be arbitrary since popovers are by default inserted as a sibling of their target element.
// So reset our font and text properties to avoid inheriting weird values.
.reset-text();
font-size: @font-size-base;
background-color: @popover-bg;
background-clip: padding-box;
border: 1px solid @popover-fallback-border-color;
border: 1px solid @popover-border-color;
border-radius: @border-radius-large;
.box-shadow(0 5px 10px rgba(0,0,0,.2));
// Offset the popover to account for the popover arrow
&.top { margin-top: -@popover-arrow-width; }
&.right { margin-left: @popover-arrow-width; }
&.bottom { margin-top: @popover-arrow-width; }
&.left { margin-left: -@popover-arrow-width; }
}
.popover-title {
margin: 0; // reset heading margin
padding: 8px 14px;
font-size: @font-size-base;
background-color: @popover-title-bg;
border-bottom: 1px solid darken(@popover-title-bg, 5%);
border-radius: (@border-radius-large - 1) (@border-radius-large - 1) 0 0;
}
.popover-content {
padding: 9px 14px;
}
// Arrows
//
// .arrow is outer, .arrow:after is inner
.popover > .arrow {
&,
&:after {
position: absolute;
display: block;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
}
}
.popover > .arrow {
border-width: @popover-arrow-outer-width;
}
.popover > .arrow:after {
border-width: @popover-arrow-width;
content: "";
}
.popover {
&.top > .arrow {
left: 50%;
margin-left: -@popover-arrow-outer-width;
border-bottom-width: 0;
border-top-color: @popover-arrow-outer-fallback-color; // IE8 fallback
border-top-color: @popover-arrow-outer-color;
bottom: -@popover-arrow-outer-width;
&:after {
content: " ";
bottom: 1px;
margin-left: -@popover-arrow-width;
border-bottom-width: 0;
border-top-color: @popover-arrow-color;
}
}
&.right > .arrow {
top: 50%;
left: -@popover-arrow-outer-width;
margin-top: -@popover-arrow-outer-width;
border-left-width: 0;
border-right-color: @popover-arrow-outer-fallback-color; // IE8 fallback
border-right-color: @popover-arrow-outer-color;
&:after {
content: " ";
left: 1px;
bottom: -@popover-arrow-width;
border-left-width: 0;
border-right-color: @popover-arrow-color;
}
}
&.bottom > .arrow {
left: 50%;
margin-left: -@popover-arrow-outer-width;
border-top-width: 0;
border-bottom-color: @popover-arrow-outer-fallback-color; // IE8 fallback
border-bottom-color: @popover-arrow-outer-color;
top: -@popover-arrow-outer-width;
&:after {
content: " ";
top: 1px;
margin-left: -@popover-arrow-width;
border-top-width: 0;
border-bottom-color: @popover-arrow-color;
}
}
&.left > .arrow {
top: 50%;
right: -@popover-arrow-outer-width;
margin-top: -@popover-arrow-outer-width;
border-right-width: 0;
border-left-color: @popover-arrow-outer-fallback-color; // IE8 fallback
border-left-color: @popover-arrow-outer-color;
&:after {
content: " ";
right: 1px;
border-right-width: 0;
border-left-color: @popover-arrow-color;
bottom: -@popover-arrow-width;
}
}
}
@@ -1,101 +0,0 @@
/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */
// ==========================================================================
// Print styles.
// Inlined to avoid the additional HTTP request: h5bp.com/r
// ==========================================================================
@media print {
*,
*:before,
*:after {
background: transparent !important;
color: #000 !important; // Black prints faster: h5bp.com/s
box-shadow: none !important;
text-shadow: none !important;
}
a,
a:visited {
text-decoration: underline;
}
a[href]:after {
content: " (" attr(href) ")";
}
abbr[title]:after {
content: " (" attr(title) ")";
}
// Don't show links that are fragment identifiers,
// or use the `javascript:` pseudo protocol
a[href^="#"]:after,
a[href^="javascript:"]:after {
content: "";
}
pre,
blockquote {
border: 1px solid #999;
page-break-inside: avoid;
}
thead {
display: table-header-group; // h5bp.com/t
}
tr,
img {
page-break-inside: avoid;
}
img {
max-width: 100% !important;
}
p,
h2,
h3 {
orphans: 3;
widows: 3;
}
h2,
h3 {
page-break-after: avoid;
}
// Bootstrap specific changes start
// Bootstrap components
.navbar {
display: none;
}
.btn,
.dropup > .btn {
> .caret {
border-top-color: #000 !important;
}
}
.label {
border: 1px solid #000;
}
.table {
border-collapse: collapse !important;
td,
th {
background-color: #fff !important;
}
}
.table-bordered {
th,
td {
border: 1px solid #ddd !important;
}
}
// Bootstrap specific changes end
}
@@ -1,87 +0,0 @@
//
// Progress bars
// --------------------------------------------------
// Bar animations
// -------------------------
// WebKit
@-webkit-keyframes progress-bar-stripes {
from { background-position: 40px 0; }
to { background-position: 0 0; }
}
// Spec and IE10+
@keyframes progress-bar-stripes {
from { background-position: 40px 0; }
to { background-position: 0 0; }
}
// Bar itself
// -------------------------
// Outer container
.progress {
overflow: hidden;
height: @line-height-computed;
margin-bottom: @line-height-computed;
background-color: @progress-bg;
border-radius: @progress-border-radius;
.box-shadow(inset 0 1px 2px rgba(0,0,0,.1));
}
// Bar of progress
.progress-bar {
float: left;
width: 0%;
height: 100%;
font-size: @font-size-small;
line-height: @line-height-computed;
color: @progress-bar-color;
text-align: center;
background-color: @progress-bar-bg;
.box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));
.transition(width .6s ease);
}
// Striped bars
//
// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the
// `.progress-bar-striped` class, which you just add to an existing
// `.progress-bar`.
.progress-striped .progress-bar,
.progress-bar-striped {
#gradient > .striped();
background-size: 40px 40px;
}
// Call animation for the active one
//
// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the
// `.progress-bar.active` approach.
.progress.active .progress-bar,
.progress-bar.active {
.animation(progress-bar-stripes 2s linear infinite);
}
// Variations
// -------------------------
.progress-bar-success {
.progress-bar-variant(@progress-bar-success-bg);
}
.progress-bar-info {
.progress-bar-variant(@progress-bar-info-bg);
}
.progress-bar-warning {
.progress-bar-variant(@progress-bar-warning-bg);
}
.progress-bar-danger {
.progress-bar-variant(@progress-bar-danger-bg);
}
@@ -1,35 +0,0 @@
// Embeds responsive
//
// Credit: Nicolas Gallagher and SUIT CSS.
.embed-responsive {
position: relative;
display: block;
height: 0;
padding: 0;
overflow: hidden;
.embed-responsive-item,
iframe,
embed,
object,
video {
position: absolute;
top: 0;
left: 0;
bottom: 0;
height: 100%;
width: 100%;
border: 0;
}
}
// Modifier class for 16:9 aspect ratio
.embed-responsive-16by9 {
padding-bottom: 56.25%;
}
// Modifier class for 4:3 aspect ratio
.embed-responsive-4by3 {
padding-bottom: 75%;
}
@@ -1,194 +0,0 @@
//
// Responsive: Utility classes
// --------------------------------------------------
// IE10 in Windows (Phone) 8
//
// Support for responsive views via media queries is kind of borked in IE10, for
// Surface/desktop in split view and for Windows Phone 8. This particular fix
// must be accompanied by a snippet of JavaScript to sniff the user agent and
// apply some conditional CSS to *only* the Surface/desktop Windows 8. Look at
// our Getting Started page for more information on this bug.
//
// For more information, see the following:
//
// Issue: https://github.com/twbs/bootstrap/issues/10497
// Docs: http://getbootstrap.com/getting-started/#support-ie10-width
// Source: http://timkadlec.com/2013/01/windows-phone-8-and-device-width/
// Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/
@-ms-viewport {
width: device-width;
}
// Visibility utilities
// Note: Deprecated .visible-xs, .visible-sm, .visible-md, and .visible-lg as of v3.2.0
.visible-xs,
.visible-sm,
.visible-md,
.visible-lg {
.responsive-invisibility();
}
.visible-xs-block,
.visible-xs-inline,
.visible-xs-inline-block,
.visible-sm-block,
.visible-sm-inline,
.visible-sm-inline-block,
.visible-md-block,
.visible-md-inline,
.visible-md-inline-block,
.visible-lg-block,
.visible-lg-inline,
.visible-lg-inline-block {
display: none !important;
}
.visible-xs {
@media (max-width: @screen-xs-max) {
.responsive-visibility();
}
}
.visible-xs-block {
@media (max-width: @screen-xs-max) {
display: block !important;
}
}
.visible-xs-inline {
@media (max-width: @screen-xs-max) {
display: inline !important;
}
}
.visible-xs-inline-block {
@media (max-width: @screen-xs-max) {
display: inline-block !important;
}
}
.visible-sm {
@media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {
.responsive-visibility();
}
}
.visible-sm-block {
@media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {
display: block !important;
}
}
.visible-sm-inline {
@media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {
display: inline !important;
}
}
.visible-sm-inline-block {
@media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {
display: inline-block !important;
}
}
.visible-md {
@media (min-width: @screen-md-min) and (max-width: @screen-md-max) {
.responsive-visibility();
}
}
.visible-md-block {
@media (min-width: @screen-md-min) and (max-width: @screen-md-max) {
display: block !important;
}
}
.visible-md-inline {
@media (min-width: @screen-md-min) and (max-width: @screen-md-max) {
display: inline !important;
}
}
.visible-md-inline-block {
@media (min-width: @screen-md-min) and (max-width: @screen-md-max) {
display: inline-block !important;
}
}
.visible-lg {
@media (min-width: @screen-lg-min) {
.responsive-visibility();
}
}
.visible-lg-block {
@media (min-width: @screen-lg-min) {
display: block !important;
}
}
.visible-lg-inline {
@media (min-width: @screen-lg-min) {
display: inline !important;
}
}
.visible-lg-inline-block {
@media (min-width: @screen-lg-min) {
display: inline-block !important;
}
}
.hidden-xs {
@media (max-width: @screen-xs-max) {
.responsive-invisibility();
}
}
.hidden-sm {
@media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {
.responsive-invisibility();
}
}
.hidden-md {
@media (min-width: @screen-md-min) and (max-width: @screen-md-max) {
.responsive-invisibility();
}
}
.hidden-lg {
@media (min-width: @screen-lg-min) {
.responsive-invisibility();
}
}
// Print utilities
//
// Media queries are placed on the inside to be mixin-friendly.
// Note: Deprecated .visible-print as of v3.2.0
.visible-print {
.responsive-invisibility();
@media print {
.responsive-visibility();
}
}
.visible-print-block {
display: none !important;
@media print {
display: block !important;
}
}
.visible-print-inline {
display: none !important;
@media print {
display: inline !important;
}
}
.visible-print-inline-block {
display: none !important;
@media print {
display: inline-block !important;
}
}
.hidden-print {
@media print {
.responsive-invisibility();
}
}
@@ -1,161 +0,0 @@
//
// Scaffolding
// --------------------------------------------------
// Reset the box-sizing
//
// Heads up! This reset may cause conflicts with some third-party widgets.
// For recommendations on resolving such conflicts, see
// http://getbootstrap.com/getting-started/#third-box-sizing
* {
.box-sizing(border-box);
}
*:before,
*:after {
.box-sizing(border-box);
}
// Body reset
html {
font-size: 10px;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
body {
font-family: @font-family-base;
font-size: @font-size-base;
line-height: @line-height-base;
color: @text-color;
background-color: @body-bg;
}
// Reset fonts for relevant elements
input,
button,
select,
textarea {
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
// Links
a {
color: @link-color;
text-decoration: none;
&:hover,
&:focus {
color: @link-hover-color;
text-decoration: @link-hover-decoration;
}
&:focus {
.tab-focus();
}
}
// Figures
//
// We reset this here because previously Normalize had no `figure` margins. This
// ensures we don't break anyone's use of the element.
figure {
margin: 0;
}
// Images
img {
vertical-align: middle;
}
// Responsive images (ensure images don't scale beyond their parents)
.img-responsive {
.img-responsive();
}
// Rounded corners
.img-rounded {
border-radius: @border-radius-large;
}
// Image thumbnails
//
// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.
.img-thumbnail {
padding: @thumbnail-padding;
line-height: @line-height-base;
background-color: @thumbnail-bg;
border: 1px solid @thumbnail-border;
border-radius: @thumbnail-border-radius;
.transition(all .2s ease-in-out);
// Keep them at most 100% wide
.img-responsive(inline-block);
}
// Perfect circle
.img-circle {
border-radius: 50%; // set radius in percents
}
// Horizontal rules
hr {
margin-top: @line-height-computed;
margin-bottom: @line-height-computed;
border: 0;
border-top: 1px solid @hr-border;
}
// Only display content to screen readers
//
// See: http://a11yproject.com/posts/how-to-hide-content/
.sr-only {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0,0,0,0);
border: 0;
}
// Use in conjunction with .sr-only to only display content when it's focused.
// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
// Credit: HTML5 Boilerplate
.sr-only-focusable {
&:active,
&:focus {
position: static;
width: auto;
height: auto;
margin: 0;
overflow: visible;
clip: auto;
}
}
// iOS "clickable elements" fix for role="button"
//
// Fixes "clickability" issue (and more generally, the firing of events such as focus as well)
// for traditionally non-focusable elements with role="button"
// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile
[role="button"] {
cursor: pointer;
}
@@ -1,234 +0,0 @@
//
// Tables
// --------------------------------------------------
table {
background-color: @table-bg;
}
caption {
padding-top: @table-cell-padding;
padding-bottom: @table-cell-padding;
color: @text-muted;
text-align: left;
}
th {
text-align: left;
}
// Baseline styles
.table {
width: 100%;
max-width: 100%;
margin-bottom: @line-height-computed;
// Cells
> thead,
> tbody,
> tfoot {
> tr {
> th,
> td {
padding: @table-cell-padding;
line-height: @line-height-base;
vertical-align: top;
border-top: 1px solid @table-border-color;
}
}
}
// Bottom align for column headings
> thead > tr > th {
vertical-align: bottom;
border-bottom: 2px solid @table-border-color;
}
// Remove top border from thead by default
> caption + thead,
> colgroup + thead,
> thead:first-child {
> tr:first-child {
> th,
> td {
border-top: 0;
}
}
}
// Account for multiple tbody instances
> tbody + tbody {
border-top: 2px solid @table-border-color;
}
// Nesting
.table {
background-color: @body-bg;
}
}
// Condensed table w/ half padding
.table-condensed {
> thead,
> tbody,
> tfoot {
> tr {
> th,
> td {
padding: @table-condensed-cell-padding;
}
}
}
}
// Bordered version
//
// Add borders all around the table and between all the columns.
.table-bordered {
border: 1px solid @table-border-color;
> thead,
> tbody,
> tfoot {
> tr {
> th,
> td {
border: 1px solid @table-border-color;
}
}
}
> thead > tr {
> th,
> td {
border-bottom-width: 2px;
}
}
}
// Zebra-striping
//
// Default zebra-stripe styles (alternating gray and transparent backgrounds)
.table-striped {
> tbody > tr:nth-of-type(odd) {
background-color: @table-bg-accent;
}
}
// Hover effect
//
// Placed here since it has to come after the potential zebra striping
.table-hover {
> tbody > tr:hover {
background-color: @table-bg-hover;
}
}
// Table cell sizing
//
// Reset default table behavior
table col[class*="col-"] {
position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)
float: none;
display: table-column;
}
table {
td,
th {
&[class*="col-"] {
position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)
float: none;
display: table-cell;
}
}
}
// Table backgrounds
//
// Exact selectors below required to override `.table-striped` and prevent
// inheritance to nested tables.
// Generate the contextual variants
.table-row-variant(active; @table-bg-active);
.table-row-variant(success; @state-success-bg);
.table-row-variant(info; @state-info-bg);
.table-row-variant(warning; @state-warning-bg);
.table-row-variant(danger; @state-danger-bg);
// Responsive tables
//
// Wrap your tables in `.table-responsive` and we'll make them mobile friendly
// by enabling horizontal scrolling. Only applies <768px. Everything above that
// will display normally.
.table-responsive {
overflow-x: auto;
min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)
@media screen and (max-width: @screen-xs-max) {
width: 100%;
margin-bottom: (@line-height-computed * 0.75);
overflow-y: hidden;
-ms-overflow-style: -ms-autohiding-scrollbar;
border: 1px solid @table-border-color;
// Tighten up spacing
> .table {
margin-bottom: 0;
// Ensure the content doesn't wrap
> thead,
> tbody,
> tfoot {
> tr {
> th,
> td {
white-space: nowrap;
}
}
}
}
// Special overrides for the bordered tables
> .table-bordered {
border: 0;
// Nuke the appropriate borders so that the parent can handle them
> thead,
> tbody,
> tfoot {
> tr {
> th:first-child,
> td:first-child {
border-left: 0;
}
> th:last-child,
> td:last-child {
border-right: 0;
}
}
}
// Only nuke the last row's bottom-border in `tbody` and `tfoot` since
// chances are there will be only one `tr` in a `thead` and that would
// remove the border altogether.
> tbody,
> tfoot {
> tr:last-child {
> th,
> td {
border-bottom: 0;
}
}
}
}
}
}
@@ -1,291 +0,0 @@
/*!
* Bootstrap v3.3.6 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
//
// Load core variables and mixins
// --------------------------------------------------
@import "variables.less";
@import "mixins.less";
//
// Buttons
// --------------------------------------------------
// Common styles
.btn-default,
.btn-primary,
.btn-success,
.btn-info,
.btn-warning,
.btn-danger {
text-shadow: 0 -1px 0 rgba(0,0,0,.2);
@shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);
.box-shadow(@shadow);
// Reset the shadow
&:active,
&.active {
.box-shadow(inset 0 3px 5px rgba(0,0,0,.125));
}
&.disabled,
&[disabled],
fieldset[disabled] & {
.box-shadow(none);
}
.badge {
text-shadow: none;
}
}
// Mixin for generating new styles
.btn-styles(@btn-color: #555) {
#gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));
.reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620
background-repeat: repeat-x;
border-color: darken(@btn-color, 14%);
&:hover,
&:focus {
background-color: darken(@btn-color, 12%);
background-position: 0 -15px;
}
&:active,
&.active {
background-color: darken(@btn-color, 12%);
border-color: darken(@btn-color, 14%);
}
&.disabled,
&[disabled],
fieldset[disabled] & {
&,
&:hover,
&:focus,
&.focus,
&:active,
&.active {
background-color: darken(@btn-color, 12%);
background-image: none;
}
}
}
// Common styles
.btn {
// Remove the gradient for the pressed/active state
&:active,
&.active {
background-image: none;
}
}
// Apply the mixin to the buttons
.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }
.btn-primary { .btn-styles(@btn-primary-bg); }
.btn-success { .btn-styles(@btn-success-bg); }
.btn-info { .btn-styles(@btn-info-bg); }
.btn-warning { .btn-styles(@btn-warning-bg); }
.btn-danger { .btn-styles(@btn-danger-bg); }
//
// Images
// --------------------------------------------------
.thumbnail,
.img-thumbnail {
.box-shadow(0 1px 2px rgba(0,0,0,.075));
}
//
// Dropdowns
// --------------------------------------------------
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
#gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));
background-color: darken(@dropdown-link-hover-bg, 5%);
}
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
#gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));
background-color: darken(@dropdown-link-active-bg, 5%);
}
//
// Navbar
// --------------------------------------------------
// Default navbar
.navbar-default {
#gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);
.reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered
border-radius: @navbar-border-radius;
@shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);
.box-shadow(@shadow);
.navbar-nav > .open > a,
.navbar-nav > .active > a {
#gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));
.box-shadow(inset 0 3px 9px rgba(0,0,0,.075));
}
}
.navbar-brand,
.navbar-nav > li > a {
text-shadow: 0 1px 0 rgba(255,255,255,.25);
}
// Inverted navbar
.navbar-inverse {
#gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);
.reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257
border-radius: @navbar-border-radius;
.navbar-nav > .open > a,
.navbar-nav > .active > a {
#gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));
.box-shadow(inset 0 3px 9px rgba(0,0,0,.25));
}
.navbar-brand,
.navbar-nav > li > a {
text-shadow: 0 -1px 0 rgba(0,0,0,.25);
}
}
// Undo rounded corners in static and fixed navbars
.navbar-static-top,
.navbar-fixed-top,
.navbar-fixed-bottom {
border-radius: 0;
}
// Fix active state of dropdown items in collapsed mode
@media (max-width: @grid-float-breakpoint-max) {
.navbar .navbar-nav .open .dropdown-menu > .active > a {
&,
&:hover,
&:focus {
color: #fff;
#gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));
}
}
}
//
// Alerts
// --------------------------------------------------
// Common styles
.alert {
text-shadow: 0 1px 0 rgba(255,255,255,.2);
@shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);
.box-shadow(@shadow);
}
// Mixin for generating new styles
.alert-styles(@color) {
#gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));
border-color: darken(@color, 15%);
}
// Apply the mixin to the alerts
.alert-success { .alert-styles(@alert-success-bg); }
.alert-info { .alert-styles(@alert-info-bg); }
.alert-warning { .alert-styles(@alert-warning-bg); }
.alert-danger { .alert-styles(@alert-danger-bg); }
//
// Progress bars
// --------------------------------------------------
// Give the progress background some depth
.progress {
#gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)
}
// Mixin for generating new styles
.progress-bar-styles(@color) {
#gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));
}
// Apply the mixin to the progress bars
.progress-bar { .progress-bar-styles(@progress-bar-bg); }
.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }
.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }
.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }
.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }
// Reset the striped class because our mixins don't do multiple gradients and
// the above custom styles override the new `.progress-bar-striped` in v3.2.0.
.progress-bar-striped {
#gradient > .striped();
}
//
// List groups
// --------------------------------------------------
.list-group {
border-radius: @border-radius-base;
.box-shadow(0 1px 2px rgba(0,0,0,.075));
}
.list-group-item.active,
.list-group-item.active:hover,
.list-group-item.active:focus {
text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);
#gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));
border-color: darken(@list-group-active-border, 7.5%);
.badge {
text-shadow: none;
}
}
//
// Panels
// --------------------------------------------------
// Common styles
.panel {
.box-shadow(0 1px 2px rgba(0,0,0,.05));
}
// Mixin for generating new styles
.panel-heading-styles(@color) {
#gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));
}
// Apply the mixin to the panel headings only
.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }
.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }
.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }
.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }
.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }
.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }
//
// Wells
// --------------------------------------------------
.well {
#gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);
border-color: darken(@well-bg, 10%);
@shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);
.box-shadow(@shadow);
}
@@ -1,36 +0,0 @@
//
// Thumbnails
// --------------------------------------------------
// Mixin and adjust the regular image class
.thumbnail {
display: block;
padding: @thumbnail-padding;
margin-bottom: @line-height-computed;
line-height: @line-height-base;
background-color: @thumbnail-bg;
border: 1px solid @thumbnail-border;
border-radius: @thumbnail-border-radius;
.transition(border .2s ease-in-out);
> img,
a > img {
&:extend(.img-responsive);
margin-left: auto;
margin-right: auto;
}
// Add a hover state for linked versions only
a&:hover,
a&:focus,
a&.active {
border-color: @link-color;
}
// Image captions
.caption {
padding: @thumbnail-caption-padding;
color: @thumbnail-caption-color;
}
}
@@ -1,101 +0,0 @@
//
// Tooltips
// --------------------------------------------------
// Base class
.tooltip {
position: absolute;
z-index: @zindex-tooltip;
display: block;
// Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
// So reset our font and text properties to avoid inheriting weird values.
.reset-text();
font-size: @font-size-small;
.opacity(0);
&.in { .opacity(@tooltip-opacity); }
&.top { margin-top: -3px; padding: @tooltip-arrow-width 0; }
&.right { margin-left: 3px; padding: 0 @tooltip-arrow-width; }
&.bottom { margin-top: 3px; padding: @tooltip-arrow-width 0; }
&.left { margin-left: -3px; padding: 0 @tooltip-arrow-width; }
}
// Wrapper for the tooltip content
.tooltip-inner {
max-width: @tooltip-max-width;
padding: 3px 8px;
color: @tooltip-color;
text-align: center;
background-color: @tooltip-bg;
border-radius: @border-radius-base;
}
// Arrows
.tooltip-arrow {
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
}
// Note: Deprecated .top-left, .top-right, .bottom-left, and .bottom-right as of v3.3.1
.tooltip {
&.top .tooltip-arrow {
bottom: 0;
left: 50%;
margin-left: -@tooltip-arrow-width;
border-width: @tooltip-arrow-width @tooltip-arrow-width 0;
border-top-color: @tooltip-arrow-color;
}
&.top-left .tooltip-arrow {
bottom: 0;
right: @tooltip-arrow-width;
margin-bottom: -@tooltip-arrow-width;
border-width: @tooltip-arrow-width @tooltip-arrow-width 0;
border-top-color: @tooltip-arrow-color;
}
&.top-right .tooltip-arrow {
bottom: 0;
left: @tooltip-arrow-width;
margin-bottom: -@tooltip-arrow-width;
border-width: @tooltip-arrow-width @tooltip-arrow-width 0;
border-top-color: @tooltip-arrow-color;
}
&.right .tooltip-arrow {
top: 50%;
left: 0;
margin-top: -@tooltip-arrow-width;
border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0;
border-right-color: @tooltip-arrow-color;
}
&.left .tooltip-arrow {
top: 50%;
right: 0;
margin-top: -@tooltip-arrow-width;
border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width;
border-left-color: @tooltip-arrow-color;
}
&.bottom .tooltip-arrow {
top: 0;
left: 50%;
margin-left: -@tooltip-arrow-width;
border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;
border-bottom-color: @tooltip-arrow-color;
}
&.bottom-left .tooltip-arrow {
top: 0;
right: @tooltip-arrow-width;
margin-top: -@tooltip-arrow-width;
border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;
border-bottom-color: @tooltip-arrow-color;
}
&.bottom-right .tooltip-arrow {
top: 0;
left: @tooltip-arrow-width;
margin-top: -@tooltip-arrow-width;
border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;
border-bottom-color: @tooltip-arrow-color;
}
}
@@ -1,302 +0,0 @@
//
// Typography
// --------------------------------------------------
// Headings
// -------------------------
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
font-family: @headings-font-family;
font-weight: @headings-font-weight;
line-height: @headings-line-height;
color: @headings-color;
small,
.small {
font-weight: normal;
line-height: 1;
color: @headings-small-color;
}
}
h1, .h1,
h2, .h2,
h3, .h3 {
margin-top: @line-height-computed;
margin-bottom: (@line-height-computed / 2);
small,
.small {
font-size: 65%;
}
}
h4, .h4,
h5, .h5,
h6, .h6 {
margin-top: (@line-height-computed / 2);
margin-bottom: (@line-height-computed / 2);
small,
.small {
font-size: 75%;
}
}
h1, .h1 { font-size: @font-size-h1; }
h2, .h2 { font-size: @font-size-h2; }
h3, .h3 { font-size: @font-size-h3; }
h4, .h4 { font-size: @font-size-h4; }
h5, .h5 { font-size: @font-size-h5; }
h6, .h6 { font-size: @font-size-h6; }
// Body text
// -------------------------
p {
margin: 0 0 (@line-height-computed / 2);
}
.lead {
margin-bottom: @line-height-computed;
font-size: floor((@font-size-base * 1.15));
font-weight: 300;
line-height: 1.4;
@media (min-width: @screen-sm-min) {
font-size: (@font-size-base * 1.5);
}
}
// Emphasis & misc
// -------------------------
// Ex: (12px small font / 14px base font) * 100% = about 85%
small,
.small {
font-size: floor((100% * @font-size-small / @font-size-base));
}
mark,
.mark {
background-color: @state-warning-bg;
padding: .2em;
}
// Alignment
.text-left { text-align: left; }
.text-right { text-align: right; }
.text-center { text-align: center; }
.text-justify { text-align: justify; }
.text-nowrap { white-space: nowrap; }
// Transformation
.text-lowercase { text-transform: lowercase; }
.text-uppercase { text-transform: uppercase; }
.text-capitalize { text-transform: capitalize; }
// Contextual colors
.text-muted {
color: @text-muted;
}
.text-primary {
.text-emphasis-variant(@brand-primary);
}
.text-success {
.text-emphasis-variant(@state-success-text);
}
.text-info {
.text-emphasis-variant(@state-info-text);
}
.text-warning {
.text-emphasis-variant(@state-warning-text);
}
.text-danger {
.text-emphasis-variant(@state-danger-text);
}
// Contextual backgrounds
// For now we'll leave these alongside the text classes until v4 when we can
// safely shift things around (per SemVer rules).
.bg-primary {
// Given the contrast here, this is the only class to have its color inverted
// automatically.
color: #fff;
.bg-variant(@brand-primary);
}
.bg-success {
.bg-variant(@state-success-bg);
}
.bg-info {
.bg-variant(@state-info-bg);
}
.bg-warning {
.bg-variant(@state-warning-bg);
}
.bg-danger {
.bg-variant(@state-danger-bg);
}
// Page header
// -------------------------
.page-header {
padding-bottom: ((@line-height-computed / 2) - 1);
margin: (@line-height-computed * 2) 0 @line-height-computed;
border-bottom: 1px solid @page-header-border-color;
}
// Lists
// -------------------------
// Unordered and Ordered lists
ul,
ol {
margin-top: 0;
margin-bottom: (@line-height-computed / 2);
ul,
ol {
margin-bottom: 0;
}
}
// List options
// Unstyled keeps list items block level, just removes default browser padding and list-style
.list-unstyled {
padding-left: 0;
list-style: none;
}
// Inline turns list items into inline-block
.list-inline {
.list-unstyled();
margin-left: -5px;
> li {
display: inline-block;
padding-left: 5px;
padding-right: 5px;
}
}
// Description Lists
dl {
margin-top: 0; // Remove browser default
margin-bottom: @line-height-computed;
}
dt,
dd {
line-height: @line-height-base;
}
dt {
font-weight: bold;
}
dd {
margin-left: 0; // Undo browser default
}
// Horizontal description lists
//
// Defaults to being stacked without any of the below styles applied, until the
// grid breakpoint is reached (default of ~768px).
.dl-horizontal {
dd {
&:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present
}
@media (min-width: @dl-horizontal-breakpoint) {
dt {
float: left;
width: (@dl-horizontal-offset - 20);
clear: left;
text-align: right;
.text-overflow();
}
dd {
margin-left: @dl-horizontal-offset;
}
}
}
// Misc
// -------------------------
// Abbreviations and acronyms
abbr[title],
// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257
abbr[data-original-title] {
cursor: help;
border-bottom: 1px dotted @abbr-border-color;
}
.initialism {
font-size: 90%;
.text-uppercase();
}
// Blockquotes
blockquote {
padding: (@line-height-computed / 2) @line-height-computed;
margin: 0 0 @line-height-computed;
font-size: @blockquote-font-size;
border-left: 5px solid @blockquote-border-color;
p,
ul,
ol {
&:last-child {
margin-bottom: 0;
}
}
// Note: Deprecated small and .small as of v3.1.0
// Context: https://github.com/twbs/bootstrap/issues/11660
footer,
small,
.small {
display: block;
font-size: 80%; // back to default font-size
line-height: @line-height-base;
color: @blockquote-small-color;
&:before {
content: '\2014 \00A0'; // em dash, nbsp
}
}
}
// Opposite alignment of blockquote
//
// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.
.blockquote-reverse,
blockquote.pull-right {
padding-right: 15px;
padding-left: 0;
border-right: 5px solid @blockquote-border-color;
border-left: 0;
text-align: right;
// Account for citation
footer,
small,
.small {
&:before { content: ''; }
&:after {
content: '\00A0 \2014'; // nbsp, em dash
}
}
}
// Addresses
address {
margin-bottom: @line-height-computed;
font-style: normal;
line-height: @line-height-base;
}
@@ -1,55 +0,0 @@
//
// Utility classes
// --------------------------------------------------
// Floats
// -------------------------
.clearfix {
.clearfix();
}
.center-block {
.center-block();
}
.pull-right {
float: right !important;
}
.pull-left {
float: left !important;
}
// Toggling content
// -------------------------
// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1
.hide {
display: none !important;
}
.show {
display: block !important;
}
.invisible {
visibility: hidden;
}
.text-hide {
.text-hide();
}
// Hide from screenreaders and browsers
//
// Credit: HTML5 Boilerplate
.hidden {
display: none !important;
}
// For Affix plugin
// -------------------------
.affix {
position: fixed;
}
@@ -1,869 +0,0 @@
//
// Variables
// --------------------------------------------------
//== Colors
//
//## Gray and brand colors for use across Bootstrap.
@gray-base: #000;
@gray-darker: lighten(@gray-base, 13.5%); // #222
@gray-dark: lighten(@gray-base, 20%); // #333
@gray: lighten(@gray-base, 33.5%); // #555
@gray-light: lighten(@gray-base, 46.7%); // #777
@gray-lighter: lighten(@gray-base, 93.5%); // #eee
@brand-primary: darken(#428bca, 6.5%); // #337ab7
@brand-success: #5cb85c;
@brand-info: #5bc0de;
@brand-warning: #f0ad4e;
@brand-danger: #d9534f;
//== Scaffolding
//
//## Settings for some of the most global styles.
//** Background color for `<body>`.
@body-bg: #fff;
//** Global text color on `<body>`.
@text-color: @gray-dark;
//** Global textual link color.
@link-color: @brand-primary;
//** Link hover color set via `darken()` function.
@link-hover-color: darken(@link-color, 15%);
//** Link hover decoration.
@link-hover-decoration: underline;
//== Typography
//
//## Font, line-height, and color for body text, headings, and more.
@font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif;
@font-family-serif: Georgia, "Times New Roman", Times, serif;
//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`.
@font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
@font-family-base: @font-family-sans-serif;
@font-size-base: 14px;
@font-size-large: ceil((@font-size-base * 1.25)); // ~18px
@font-size-small: ceil((@font-size-base * 0.85)); // ~12px
@font-size-h1: floor((@font-size-base * 2.6)); // ~36px
@font-size-h2: floor((@font-size-base * 2.15)); // ~30px
@font-size-h3: ceil((@font-size-base * 1.7)); // ~24px
@font-size-h4: ceil((@font-size-base * 1.25)); // ~18px
@font-size-h5: @font-size-base;
@font-size-h6: ceil((@font-size-base * 0.85)); // ~12px
//** Unit-less `line-height` for use in components like buttons.
@line-height-base: 1.428571429; // 20/14
//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
@line-height-computed: floor((@font-size-base * @line-height-base)); // ~20px
//** By default, this inherits from the `<body>`.
@headings-font-family: inherit;
@headings-font-weight: 500;
@headings-line-height: 1.1;
@headings-color: inherit;
//== Iconography
//
//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
//** Load fonts from this directory.
@icon-font-path: "../fonts/";
//** File name for all font files.
@icon-font-name: "glyphicons-halflings-regular";
//** Element ID within SVG icon file.
@icon-font-svg-id: "glyphicons_halflingsregular";
//== Components
//
//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
@padding-base-vertical: 6px;
@padding-base-horizontal: 12px;
@padding-large-vertical: 10px;
@padding-large-horizontal: 16px;
@padding-small-vertical: 5px;
@padding-small-horizontal: 10px;
@padding-xs-vertical: 1px;
@padding-xs-horizontal: 5px;
@line-height-large: 1.3333333; // extra decimals for Win 8.1 Chrome
@line-height-small: 1.5;
@border-radius-base: 4px;
@border-radius-large: 6px;
@border-radius-small: 3px;
//** Global color for active items (e.g., navs or dropdowns).
@component-active-color: #fff;
//** Global background color for active items (e.g., navs or dropdowns).
@component-active-bg: @brand-primary;
//** Width of the `border` for generating carets that indicator dropdowns.
@caret-width-base: 4px;
//** Carets increase slightly in size for larger components.
@caret-width-large: 5px;
//== Tables
//
//## Customizes the `.table` component with basic values, each used across all table variations.
//** Padding for `<th>`s and `<td>`s.
@table-cell-padding: 8px;
//** Padding for cells in `.table-condensed`.
@table-condensed-cell-padding: 5px;
//** Default background color used for all tables.
@table-bg: transparent;
//** Background color used for `.table-striped`.
@table-bg-accent: #f9f9f9;
//** Background color used for `.table-hover`.
@table-bg-hover: #f5f5f5;
@table-bg-active: @table-bg-hover;
//** Border color for table and cell borders.
@table-border-color: #ddd;
//== Buttons
//
//## For each of Bootstrap's buttons, define text, background and border color.
@btn-font-weight: normal;
@btn-default-color: #333;
@btn-default-bg: #fff;
@btn-default-border: #ccc;
@btn-primary-color: #fff;
@btn-primary-bg: @brand-primary;
@btn-primary-border: darken(@btn-primary-bg, 5%);
@btn-success-color: #fff;
@btn-success-bg: @brand-success;
@btn-success-border: darken(@btn-success-bg, 5%);
@btn-info-color: #fff;
@btn-info-bg: @brand-info;
@btn-info-border: darken(@btn-info-bg, 5%);
@btn-warning-color: #fff;
@btn-warning-bg: @brand-warning;
@btn-warning-border: darken(@btn-warning-bg, 5%);
@btn-danger-color: #fff;
@btn-danger-bg: @brand-danger;
@btn-danger-border: darken(@btn-danger-bg, 5%);
@btn-link-disabled-color: @gray-light;
// Allows for customizing button radius independently from global border radius
@btn-border-radius-base: @border-radius-base;
@btn-border-radius-large: @border-radius-large;
@btn-border-radius-small: @border-radius-small;
//== Forms
//
//##
//** `<input>` background color
@input-bg: #fff;
//** `<input disabled>` background color
@input-bg-disabled: @gray-lighter;
//** Text color for `<input>`s
@input-color: @gray;
//** `<input>` border color
@input-border: #ccc;
// TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4
//** Default `.form-control` border radius
// This has no effect on `<select>`s in some browsers, due to the limited stylability of `<select>`s in CSS.
@input-border-radius: @border-radius-base;
//** Large `.form-control` border radius
@input-border-radius-large: @border-radius-large;
//** Small `.form-control` border radius
@input-border-radius-small: @border-radius-small;
//** Border color for inputs on focus
@input-border-focus: #66afe9;
//** Placeholder text color
@input-color-placeholder: #999;
//** Default `.form-control` height
@input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2);
//** Large `.form-control` height
@input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);
//** Small `.form-control` height
@input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);
//** `.form-group` margin
@form-group-margin-bottom: 15px;
@legend-color: @gray-dark;
@legend-border-color: #e5e5e5;
//** Background color for textual input addons
@input-group-addon-bg: @gray-lighter;
//** Border color for textual input addons
@input-group-addon-border-color: @input-border;
//** Disabled cursor for form controls and buttons.
@cursor-disabled: not-allowed;
//== Dropdowns
//
//## Dropdown menu container and contents.
//** Background for the dropdown menu.
@dropdown-bg: #fff;
//** Dropdown menu `border-color`.
@dropdown-border: rgba(0,0,0,.15);
//** Dropdown menu `border-color` **for IE8**.
@dropdown-fallback-border: #ccc;
//** Divider color for between dropdown items.
@dropdown-divider-bg: #e5e5e5;
//** Dropdown link text color.
@dropdown-link-color: @gray-dark;
//** Hover color for dropdown links.
@dropdown-link-hover-color: darken(@gray-dark, 5%);
//** Hover background for dropdown links.
@dropdown-link-hover-bg: #f5f5f5;
//** Active dropdown menu item text color.
@dropdown-link-active-color: @component-active-color;
//** Active dropdown menu item background color.
@dropdown-link-active-bg: @component-active-bg;
//** Disabled dropdown menu item background color.
@dropdown-link-disabled-color: @gray-light;
//** Text color for headers within dropdown menus.
@dropdown-header-color: @gray-light;
//** Deprecated `@dropdown-caret-color` as of v3.1.0
@dropdown-caret-color: #000;
//-- Z-index master list
//
// Warning: Avoid customizing these values. They're used for a bird's eye view
// of components dependent on the z-axis and are designed to all work together.
//
// Note: These variables are not generated into the Customizer.
@zindex-navbar: 1000;
@zindex-dropdown: 1000;
@zindex-popover: 1060;
@zindex-tooltip: 1070;
@zindex-navbar-fixed: 1030;
@zindex-modal-background: 1040;
@zindex-modal: 1050;
//== Media queries breakpoints
//
//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
// Extra small screen / phone
//** Deprecated `@screen-xs` as of v3.0.1
@screen-xs: 480px;
//** Deprecated `@screen-xs-min` as of v3.2.0
@screen-xs-min: @screen-xs;
//** Deprecated `@screen-phone` as of v3.0.1
@screen-phone: @screen-xs-min;
// Small screen / tablet
//** Deprecated `@screen-sm` as of v3.0.1
@screen-sm: 768px;
@screen-sm-min: @screen-sm;
//** Deprecated `@screen-tablet` as of v3.0.1
@screen-tablet: @screen-sm-min;
// Medium screen / desktop
//** Deprecated `@screen-md` as of v3.0.1
@screen-md: 992px;
@screen-md-min: @screen-md;
//** Deprecated `@screen-desktop` as of v3.0.1
@screen-desktop: @screen-md-min;
// Large screen / wide desktop
//** Deprecated `@screen-lg` as of v3.0.1
@screen-lg: 1200px;
@screen-lg-min: @screen-lg;
//** Deprecated `@screen-lg-desktop` as of v3.0.1
@screen-lg-desktop: @screen-lg-min;
// So media queries don't overlap when required, provide a maximum
@screen-xs-max: (@screen-sm-min - 1);
@screen-sm-max: (@screen-md-min - 1);
@screen-md-max: (@screen-lg-min - 1);
//== Grid system
//
//## Define your custom responsive grid.
//** Number of columns in the grid.
@grid-columns: 12;
//** Padding between columns. Gets divided in half for the left and right.
@grid-gutter-width: 30px;
// Navbar collapse
//** Point at which the navbar becomes uncollapsed.
@grid-float-breakpoint: @screen-sm-min;
//** Point at which the navbar begins collapsing.
@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
//== Container sizes
//
//## Define the maximum width of `.container` for different screen sizes.
// Small screen / tablet
@container-tablet: (720px + @grid-gutter-width);
//** For `@screen-sm-min` and up.
@container-sm: @container-tablet;
// Medium screen / desktop
@container-desktop: (940px + @grid-gutter-width);
//** For `@screen-md-min` and up.
@container-md: @container-desktop;
// Large screen / wide desktop
@container-large-desktop: (1140px + @grid-gutter-width);
//** For `@screen-lg-min` and up.
@container-lg: @container-large-desktop;
//== Navbar
//
//##
// Basics of a navbar
@navbar-height: 50px;
@navbar-margin-bottom: @line-height-computed;
@navbar-border-radius: @border-radius-base;
@navbar-padding-horizontal: floor((@grid-gutter-width / 2));
@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2);
@navbar-collapse-max-height: 340px;
@navbar-default-color: #777;
@navbar-default-bg: #f8f8f8;
@navbar-default-border: darken(@navbar-default-bg, 6.5%);
// Navbar links
@navbar-default-link-color: #777;
@navbar-default-link-hover-color: #333;
@navbar-default-link-hover-bg: transparent;
@navbar-default-link-active-color: #555;
@navbar-default-link-active-bg: darken(@navbar-default-bg, 6.5%);
@navbar-default-link-disabled-color: #ccc;
@navbar-default-link-disabled-bg: transparent;
// Navbar brand label
@navbar-default-brand-color: @navbar-default-link-color;
@navbar-default-brand-hover-color: darken(@navbar-default-brand-color, 10%);
@navbar-default-brand-hover-bg: transparent;
// Navbar toggle
@navbar-default-toggle-hover-bg: #ddd;
@navbar-default-toggle-icon-bar-bg: #888;
@navbar-default-toggle-border-color: #ddd;
//=== Inverted navbar
// Reset inverted navbar basics
@navbar-inverse-color: lighten(@gray-light, 15%);
@navbar-inverse-bg: #222;
@navbar-inverse-border: darken(@navbar-inverse-bg, 10%);
// Inverted navbar links
@navbar-inverse-link-color: lighten(@gray-light, 15%);
@navbar-inverse-link-hover-color: #fff;
@navbar-inverse-link-hover-bg: transparent;
@navbar-inverse-link-active-color: @navbar-inverse-link-hover-color;
@navbar-inverse-link-active-bg: darken(@navbar-inverse-bg, 10%);
@navbar-inverse-link-disabled-color: #444;
@navbar-inverse-link-disabled-bg: transparent;
// Inverted navbar brand label
@navbar-inverse-brand-color: @navbar-inverse-link-color;
@navbar-inverse-brand-hover-color: #fff;
@navbar-inverse-brand-hover-bg: transparent;
// Inverted navbar toggle
@navbar-inverse-toggle-hover-bg: #333;
@navbar-inverse-toggle-icon-bar-bg: #fff;
@navbar-inverse-toggle-border-color: #333;
//== Navs
//
//##
//=== Shared nav styles
@nav-link-padding: 10px 15px;
@nav-link-hover-bg: @gray-lighter;
@nav-disabled-link-color: @gray-light;
@nav-disabled-link-hover-color: @gray-light;
//== Tabs
@nav-tabs-border-color: #ddd;
@nav-tabs-link-hover-border-color: @gray-lighter;
@nav-tabs-active-link-hover-bg: @body-bg;
@nav-tabs-active-link-hover-color: @gray;
@nav-tabs-active-link-hover-border-color: #ddd;
@nav-tabs-justified-link-border-color: #ddd;
@nav-tabs-justified-active-link-border-color: @body-bg;
//== Pills
@nav-pills-border-radius: @border-radius-base;
@nav-pills-active-link-hover-bg: @component-active-bg;
@nav-pills-active-link-hover-color: @component-active-color;
//== Pagination
//
//##
@pagination-color: @link-color;
@pagination-bg: #fff;
@pagination-border: #ddd;
@pagination-hover-color: @link-hover-color;
@pagination-hover-bg: @gray-lighter;
@pagination-hover-border: #ddd;
@pagination-active-color: #fff;
@pagination-active-bg: @brand-primary;
@pagination-active-border: @brand-primary;
@pagination-disabled-color: @gray-light;
@pagination-disabled-bg: #fff;
@pagination-disabled-border: #ddd;
//== Pager
//
//##
@pager-bg: @pagination-bg;
@pager-border: @pagination-border;
@pager-border-radius: 15px;
@pager-hover-bg: @pagination-hover-bg;
@pager-active-bg: @pagination-active-bg;
@pager-active-color: @pagination-active-color;
@pager-disabled-color: @pagination-disabled-color;
//== Jumbotron
//
//##
@jumbotron-padding: 30px;
@jumbotron-color: inherit;
@jumbotron-bg: @gray-lighter;
@jumbotron-heading-color: inherit;
@jumbotron-font-size: ceil((@font-size-base * 1.5));
@jumbotron-heading-font-size: ceil((@font-size-base * 4.5));
//== Form states and alerts
//
//## Define colors for form feedback states and, by default, alerts.
@state-success-text: #3c763d;
@state-success-bg: #dff0d8;
@state-success-border: darken(spin(@state-success-bg, -10), 5%);
@state-info-text: #31708f;
@state-info-bg: #d9edf7;
@state-info-border: darken(spin(@state-info-bg, -10), 7%);
@state-warning-text: #8a6d3b;
@state-warning-bg: #fcf8e3;
@state-warning-border: darken(spin(@state-warning-bg, -10), 5%);
@state-danger-text: #a94442;
@state-danger-bg: #f2dede;
@state-danger-border: darken(spin(@state-danger-bg, -10), 5%);
//== Tooltips
//
//##
//** Tooltip max width
@tooltip-max-width: 200px;
//** Tooltip text color
@tooltip-color: #fff;
//** Tooltip background color
@tooltip-bg: #000;
@tooltip-opacity: .9;
//** Tooltip arrow width
@tooltip-arrow-width: 5px;
//** Tooltip arrow color
@tooltip-arrow-color: @tooltip-bg;
//== Popovers
//
//##
//** Popover body background color
@popover-bg: #fff;
//** Popover maximum width
@popover-max-width: 276px;
//** Popover border color
@popover-border-color: rgba(0,0,0,.2);
//** Popover fallback border color
@popover-fallback-border-color: #ccc;
//** Popover title background color
@popover-title-bg: darken(@popover-bg, 3%);
//** Popover arrow width
@popover-arrow-width: 10px;
//** Popover arrow color
@popover-arrow-color: @popover-bg;
//** Popover outer arrow width
@popover-arrow-outer-width: (@popover-arrow-width + 1);
//** Popover outer arrow color
@popover-arrow-outer-color: fadein(@popover-border-color, 5%);
//** Popover outer arrow fallback color
@popover-arrow-outer-fallback-color: darken(@popover-fallback-border-color, 20%);
//== Labels
//
//##
//** Default label background color
@label-default-bg: @gray-light;
//** Primary label background color
@label-primary-bg: @brand-primary;
//** Success label background color
@label-success-bg: @brand-success;
//** Info label background color
@label-info-bg: @brand-info;
//** Warning label background color
@label-warning-bg: @brand-warning;
//** Danger label background color
@label-danger-bg: @brand-danger;
//** Default label text color
@label-color: #fff;
//** Default text color of a linked label
@label-link-hover-color: #fff;
//== Modals
//
//##
//** Padding applied to the modal body
@modal-inner-padding: 15px;
//** Padding applied to the modal title
@modal-title-padding: 15px;
//** Modal title line-height
@modal-title-line-height: @line-height-base;
//** Background color of modal content area
@modal-content-bg: #fff;
//** Modal content border color
@modal-content-border-color: rgba(0,0,0,.2);
//** Modal content border color **for IE8**
@modal-content-fallback-border-color: #999;
//** Modal backdrop background color
@modal-backdrop-bg: #000;
//** Modal backdrop opacity
@modal-backdrop-opacity: .5;
//** Modal header border color
@modal-header-border-color: #e5e5e5;
//** Modal footer border color
@modal-footer-border-color: @modal-header-border-color;
@modal-lg: 900px;
@modal-md: 600px;
@modal-sm: 300px;
//== Alerts
//
//## Define alert colors, border radius, and padding.
@alert-padding: 15px;
@alert-border-radius: @border-radius-base;
@alert-link-font-weight: bold;
@alert-success-bg: @state-success-bg;
@alert-success-text: @state-success-text;
@alert-success-border: @state-success-border;
@alert-info-bg: @state-info-bg;
@alert-info-text: @state-info-text;
@alert-info-border: @state-info-border;
@alert-warning-bg: @state-warning-bg;
@alert-warning-text: @state-warning-text;
@alert-warning-border: @state-warning-border;
@alert-danger-bg: @state-danger-bg;
@alert-danger-text: @state-danger-text;
@alert-danger-border: @state-danger-border;
//== Progress bars
//
//##
//** Background color of the whole progress component
@progress-bg: #f5f5f5;
//** Progress bar text color
@progress-bar-color: #fff;
//** Variable for setting rounded corners on progress bar.
@progress-border-radius: @border-radius-base;
//** Default progress bar color
@progress-bar-bg: @brand-primary;
//** Success progress bar color
@progress-bar-success-bg: @brand-success;
//** Warning progress bar color
@progress-bar-warning-bg: @brand-warning;
//** Danger progress bar color
@progress-bar-danger-bg: @brand-danger;
//** Info progress bar color
@progress-bar-info-bg: @brand-info;
//== List group
//
//##
//** Background color on `.list-group-item`
@list-group-bg: #fff;
//** `.list-group-item` border color
@list-group-border: #ddd;
//** List group border radius
@list-group-border-radius: @border-radius-base;
//** Background color of single list items on hover
@list-group-hover-bg: #f5f5f5;
//** Text color of active list items
@list-group-active-color: @component-active-color;
//** Background color of active list items
@list-group-active-bg: @component-active-bg;
//** Border color of active list elements
@list-group-active-border: @list-group-active-bg;
//** Text color for content within active list items
@list-group-active-text-color: lighten(@list-group-active-bg, 40%);
//** Text color of disabled list items
@list-group-disabled-color: @gray-light;
//** Background color of disabled list items
@list-group-disabled-bg: @gray-lighter;
//** Text color for content within disabled list items
@list-group-disabled-text-color: @list-group-disabled-color;
@list-group-link-color: #555;
@list-group-link-hover-color: @list-group-link-color;
@list-group-link-heading-color: #333;
//== Panels
//
//##
@panel-bg: #fff;
@panel-body-padding: 15px;
@panel-heading-padding: 10px 15px;
@panel-footer-padding: @panel-heading-padding;
@panel-border-radius: @border-radius-base;
//** Border color for elements within panels
@panel-inner-border: #ddd;
@panel-footer-bg: #f5f5f5;
@panel-default-text: @gray-dark;
@panel-default-border: #ddd;
@panel-default-heading-bg: #f5f5f5;
@panel-primary-text: #fff;
@panel-primary-border: @brand-primary;
@panel-primary-heading-bg: @brand-primary;
@panel-success-text: @state-success-text;
@panel-success-border: @state-success-border;
@panel-success-heading-bg: @state-success-bg;
@panel-info-text: @state-info-text;
@panel-info-border: @state-info-border;
@panel-info-heading-bg: @state-info-bg;
@panel-warning-text: @state-warning-text;
@panel-warning-border: @state-warning-border;
@panel-warning-heading-bg: @state-warning-bg;
@panel-danger-text: @state-danger-text;
@panel-danger-border: @state-danger-border;
@panel-danger-heading-bg: @state-danger-bg;
//== Thumbnails
//
//##
//** Padding around the thumbnail image
@thumbnail-padding: 4px;
//** Thumbnail background color
@thumbnail-bg: @body-bg;
//** Thumbnail border color
@thumbnail-border: #ddd;
//** Thumbnail border radius
@thumbnail-border-radius: @border-radius-base;
//** Custom text color for thumbnail captions
@thumbnail-caption-color: @text-color;
//** Padding around the thumbnail caption
@thumbnail-caption-padding: 9px;
//== Wells
//
//##
@well-bg: #f5f5f5;
@well-border: darken(@well-bg, 7%);
//== Badges
//
//##
@badge-color: #fff;
//** Linked badge text color on hover
@badge-link-hover-color: #fff;
@badge-bg: @gray-light;
//** Badge text color in active nav link
@badge-active-color: @link-color;
//** Badge background color in active nav link
@badge-active-bg: #fff;
@badge-font-weight: bold;
@badge-line-height: 1;
@badge-border-radius: 10px;
//== Breadcrumbs
//
//##
@breadcrumb-padding-vertical: 8px;
@breadcrumb-padding-horizontal: 15px;
//** Breadcrumb background color
@breadcrumb-bg: #f5f5f5;
//** Breadcrumb text color
@breadcrumb-color: #ccc;
//** Text color of current page in the breadcrumb
@breadcrumb-active-color: @gray-light;
//** Textual separator for between breadcrumb elements
@breadcrumb-separator: "/";
//== Carousel
//
//##
@carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6);
@carousel-control-color: #fff;
@carousel-control-width: 15%;
@carousel-control-opacity: .5;
@carousel-control-font-size: 20px;
@carousel-indicator-active-bg: #fff;
@carousel-indicator-border-color: #fff;
@carousel-caption-color: #fff;
//== Close
//
//##
@close-font-weight: bold;
@close-color: #000;
@close-text-shadow: 0 1px 0 #fff;
//== Code
//
//##
@code-color: #c7254e;
@code-bg: #f9f2f4;
@kbd-color: #fff;
@kbd-bg: #333;
@pre-bg: #f5f5f5;
@pre-color: @gray-dark;
@pre-border-color: #ccc;
@pre-scrollable-max-height: 340px;
//== Type
//
//##
//** Horizontal offset for forms and lists.
@component-offset-horizontal: 180px;
//** Text muted color
@text-muted: @gray-light;
//** Abbreviations and acronyms border color
@abbr-border-color: @gray-light;
//** Headings small color
@headings-small-color: @gray-light;
//** Blockquote small color
@blockquote-small-color: @gray-light;
//** Blockquote font size
@blockquote-font-size: (@font-size-base * 1.25);
//** Blockquote border color
@blockquote-border-color: @gray-lighter;
//** Page header border color
@page-header-border-color: @gray-lighter;
//** Width of horizontal description list titles
@dl-horizontal-offset: @component-offset-horizontal;
//** Point at which .dl-horizontal becomes horizontal
@dl-horizontal-breakpoint: @grid-float-breakpoint;
//** Horizontal line color.
@hr-border: @gray-lighter;

Some files were not shown because too many files have changed in this diff Show More