From b79ac583e82f52824c0411578200aa0fadc73766 Mon Sep 17 00:00:00 2001
From: Martin Zimmermann
Date: Sun, 24 Nov 2013 21:43:57 +0100
Subject: [PATCH] use sphinx for documentation
---
Makefile | 3 +
README.md | 60 -
docs/API.md | 100 --
docs/_isso/html5.py | 1298 +++++++++++++++++
docs/_isso/layout.html | 123 ++
docs/_isso/page.html | 16 +
docs/_isso/search.html | 54 +
docs/_isso/searchbox.html | 12 +
docs/_isso/sidebar-docs.html | 27 +
docs/_isso/theme.conf | 3 +
.../bourbon/_bourbon-deprecated-upcoming.scss | 13 +
docs/_static/css/bourbon/_bourbon.scss | 59 +
docs/_static/css/bourbon/addons/_button.scss | 273 ++++
.../_static/css/bourbon/addons/_clearfix.scss | 29 +
.../css/bourbon/addons/_font-family.scss | 5 +
.../css/bourbon/addons/_hide-text.scss | 5 +
.../bourbon/addons/_html5-input-types.scss | 56 +
.../_static/css/bourbon/addons/_position.scss | 42 +
.../_static/css/bourbon/addons/_prefixer.scss | 49 +
.../css/bourbon/addons/_retina-image.scss | 32 +
docs/_static/css/bourbon/addons/_size.scss | 44 +
.../css/bourbon/addons/_timing-functions.scss | 32 +
.../_static/css/bourbon/addons/_triangle.scss | 45 +
docs/_static/css/bourbon/css3/_animation.scss | 52 +
.../_static/css/bourbon/css3/_appearance.scss | 3 +
.../bourbon/css3/_backface-visibility.scss | 6 +
.../css/bourbon/css3/_background-image.scss | 48 +
.../_static/css/bourbon/css3/_background.scss | 103 ++
.../css/bourbon/css3/_border-image.scss | 55 +
.../css/bourbon/css3/_border-radius.scss | 22 +
.../_static/css/bourbon/css3/_box-sizing.scss | 4 +
docs/_static/css/bourbon/css3/_columns.scss | 47 +
docs/_static/css/bourbon/css3/_flex-box.scss | 52 +
docs/_static/css/bourbon/css3/_font-face.scss | 23 +
.../css/bourbon/css3/_hidpi-media-query.scss | 10 +
.../css/bourbon/css3/_image-rendering.scss | 13 +
.../css/bourbon/css3/_inline-block.scss | 8 +
docs/_static/css/bourbon/css3/_keyframes.scss | 43 +
.../css/bourbon/css3/_linear-gradient.scss | 41 +
.../css/bourbon/css3/_perspective.scss | 8 +
.../css/bourbon/css3/_placeholder.scss | 29 +
.../css/bourbon/css3/_radial-gradient.scss | 44 +
docs/_static/css/bourbon/css3/_transform.scss | 15 +
.../_static/css/bourbon/css3/_transition.scss | 34 +
.../css/bourbon/css3/_user-select.scss | 3 +
.../css/bourbon/functions/_compact.scss | 11 +
.../css/bourbon/functions/_flex-grid.scss | 39 +
.../css/bourbon/functions/_grid-width.scss | 13 +
.../bourbon/functions/_linear-gradient.scss | 13 +
.../css/bourbon/functions/_modular-scale.scss | 40 +
.../css/bourbon/functions/_px-to-em.scss | 8 +
.../bourbon/functions/_radial-gradient.scss | 23 +
.../css/bourbon/functions/_tint-shade.scss | 9 +
.../functions/_transition-property-name.scss | 22 +
.../helpers/_deprecated-webkit-gradient.scss | 39 +
.../helpers/_gradient-positions-parser.scss | 13 +
.../helpers/_linear-positions-parser.scss | 61 +
.../bourbon/helpers/_radial-arg-parser.scss | 69 +
.../helpers/_radial-positions-parser.scss | 18 +
.../bourbon/helpers/_render-gradients.scss | 26 +
.../bourbon/helpers/_shape-size-stripper.scss | 10 +
docs/_static/css/neat/_neat-helpers.scss | 8 +
docs/_static/css/neat/_neat.scss | 21 +
.../css/neat/functions/_new-breakpoint.scss | 16 +
docs/_static/css/neat/functions/_private.scss | 107 ++
.../_static/css/neat/functions/_px-to-em.scss | 3 +
docs/_static/css/neat/grid/_fill-parent.scss | 7 +
docs/_static/css/neat/grid/_grid.scss | 5 +
docs/_static/css/neat/grid/_media.scss | 51 +
docs/_static/css/neat/grid/_omega.scss | 79 +
.../css/neat/grid/_outer-container.scss | 8 +
docs/_static/css/neat/grid/_pad.scss | 8 +
docs/_static/css/neat/grid/_private.scss | 50 +
docs/_static/css/neat/grid/_reset.scss | 12 +
docs/_static/css/neat/grid/_row.scss | 17 +
docs/_static/css/neat/grid/_shift.scss | 9 +
docs/_static/css/neat/grid/_span-columns.scss | 38 +
docs/_static/css/neat/grid/_to-deprecate.scss | 57 +
docs/_static/css/neat/grid/_visual-grid.scss | 41 +
docs/_static/css/neat/settings/_grid.scss | 7 +
.../css/neat/settings/_visual-grid.scss | 5 +
docs/_static/css/site.css | 118 ++
docs/_static/css/site.scss | 325 +++++
docs/_static/duty_calls.png | Bin 0 -> 14103 bytes
docs/conf.py | 258 ++++
docs/contribute.rst | 4 +
docs/docs/configuration/client.rst | 52 +
.../configuration/server.rst} | 80 +-
docs/docs/configuration/setup.rst | 90 ++
docs/docs/extras/api.rst | 98 ++
docs/docs/extras/multi-site.rst | 56 +
docs/docs/extras/uwsgi.rst | 57 +
docs/docs/index.rst | 47 +
docs/docs/install.rst | 24 +
docs/docs/quickstart.rst | 187 +++
docs/docs/troubleshooting.rst | 4 +
docs/docs/usage.rst | 2 +
docs/index.html | 60 +
docs/news.rst | 4 +
docs/uWSGI.md | 55 -
100 files changed, 5246 insertions(+), 281 deletions(-)
delete mode 100644 docs/API.md
create mode 100644 docs/_isso/html5.py
create mode 100644 docs/_isso/layout.html
create mode 100644 docs/_isso/page.html
create mode 100644 docs/_isso/search.html
create mode 100644 docs/_isso/searchbox.html
create mode 100644 docs/_isso/sidebar-docs.html
create mode 100644 docs/_isso/theme.conf
create mode 100644 docs/_static/css/bourbon/_bourbon-deprecated-upcoming.scss
create mode 100644 docs/_static/css/bourbon/_bourbon.scss
create mode 100644 docs/_static/css/bourbon/addons/_button.scss
create mode 100644 docs/_static/css/bourbon/addons/_clearfix.scss
create mode 100644 docs/_static/css/bourbon/addons/_font-family.scss
create mode 100644 docs/_static/css/bourbon/addons/_hide-text.scss
create mode 100644 docs/_static/css/bourbon/addons/_html5-input-types.scss
create mode 100644 docs/_static/css/bourbon/addons/_position.scss
create mode 100644 docs/_static/css/bourbon/addons/_prefixer.scss
create mode 100644 docs/_static/css/bourbon/addons/_retina-image.scss
create mode 100644 docs/_static/css/bourbon/addons/_size.scss
create mode 100644 docs/_static/css/bourbon/addons/_timing-functions.scss
create mode 100644 docs/_static/css/bourbon/addons/_triangle.scss
create mode 100644 docs/_static/css/bourbon/css3/_animation.scss
create mode 100644 docs/_static/css/bourbon/css3/_appearance.scss
create mode 100644 docs/_static/css/bourbon/css3/_backface-visibility.scss
create mode 100644 docs/_static/css/bourbon/css3/_background-image.scss
create mode 100644 docs/_static/css/bourbon/css3/_background.scss
create mode 100644 docs/_static/css/bourbon/css3/_border-image.scss
create mode 100644 docs/_static/css/bourbon/css3/_border-radius.scss
create mode 100644 docs/_static/css/bourbon/css3/_box-sizing.scss
create mode 100644 docs/_static/css/bourbon/css3/_columns.scss
create mode 100644 docs/_static/css/bourbon/css3/_flex-box.scss
create mode 100644 docs/_static/css/bourbon/css3/_font-face.scss
create mode 100644 docs/_static/css/bourbon/css3/_hidpi-media-query.scss
create mode 100644 docs/_static/css/bourbon/css3/_image-rendering.scss
create mode 100644 docs/_static/css/bourbon/css3/_inline-block.scss
create mode 100644 docs/_static/css/bourbon/css3/_keyframes.scss
create mode 100644 docs/_static/css/bourbon/css3/_linear-gradient.scss
create mode 100644 docs/_static/css/bourbon/css3/_perspective.scss
create mode 100644 docs/_static/css/bourbon/css3/_placeholder.scss
create mode 100644 docs/_static/css/bourbon/css3/_radial-gradient.scss
create mode 100644 docs/_static/css/bourbon/css3/_transform.scss
create mode 100644 docs/_static/css/bourbon/css3/_transition.scss
create mode 100644 docs/_static/css/bourbon/css3/_user-select.scss
create mode 100644 docs/_static/css/bourbon/functions/_compact.scss
create mode 100644 docs/_static/css/bourbon/functions/_flex-grid.scss
create mode 100644 docs/_static/css/bourbon/functions/_grid-width.scss
create mode 100644 docs/_static/css/bourbon/functions/_linear-gradient.scss
create mode 100644 docs/_static/css/bourbon/functions/_modular-scale.scss
create mode 100644 docs/_static/css/bourbon/functions/_px-to-em.scss
create mode 100644 docs/_static/css/bourbon/functions/_radial-gradient.scss
create mode 100644 docs/_static/css/bourbon/functions/_tint-shade.scss
create mode 100644 docs/_static/css/bourbon/functions/_transition-property-name.scss
create mode 100644 docs/_static/css/bourbon/helpers/_deprecated-webkit-gradient.scss
create mode 100644 docs/_static/css/bourbon/helpers/_gradient-positions-parser.scss
create mode 100644 docs/_static/css/bourbon/helpers/_linear-positions-parser.scss
create mode 100644 docs/_static/css/bourbon/helpers/_radial-arg-parser.scss
create mode 100644 docs/_static/css/bourbon/helpers/_radial-positions-parser.scss
create mode 100644 docs/_static/css/bourbon/helpers/_render-gradients.scss
create mode 100644 docs/_static/css/bourbon/helpers/_shape-size-stripper.scss
create mode 100644 docs/_static/css/neat/_neat-helpers.scss
create mode 100644 docs/_static/css/neat/_neat.scss
create mode 100644 docs/_static/css/neat/functions/_new-breakpoint.scss
create mode 100644 docs/_static/css/neat/functions/_private.scss
create mode 100644 docs/_static/css/neat/functions/_px-to-em.scss
create mode 100644 docs/_static/css/neat/grid/_fill-parent.scss
create mode 100644 docs/_static/css/neat/grid/_grid.scss
create mode 100644 docs/_static/css/neat/grid/_media.scss
create mode 100644 docs/_static/css/neat/grid/_omega.scss
create mode 100644 docs/_static/css/neat/grid/_outer-container.scss
create mode 100644 docs/_static/css/neat/grid/_pad.scss
create mode 100644 docs/_static/css/neat/grid/_private.scss
create mode 100644 docs/_static/css/neat/grid/_reset.scss
create mode 100644 docs/_static/css/neat/grid/_row.scss
create mode 100644 docs/_static/css/neat/grid/_shift.scss
create mode 100644 docs/_static/css/neat/grid/_span-columns.scss
create mode 100644 docs/_static/css/neat/grid/_to-deprecate.scss
create mode 100644 docs/_static/css/neat/grid/_visual-grid.scss
create mode 100644 docs/_static/css/neat/settings/_grid.scss
create mode 100644 docs/_static/css/neat/settings/_visual-grid.scss
create mode 100644 docs/_static/css/site.css
create mode 100644 docs/_static/css/site.scss
create mode 100644 docs/_static/duty_calls.png
create mode 100644 docs/conf.py
create mode 100644 docs/contribute.rst
create mode 100644 docs/docs/configuration/client.rst
rename docs/{CONFIGURATION.rst => docs/configuration/server.rst} (71%)
create mode 100644 docs/docs/configuration/setup.rst
create mode 100644 docs/docs/extras/api.rst
create mode 100644 docs/docs/extras/multi-site.rst
create mode 100644 docs/docs/extras/uwsgi.rst
create mode 100644 docs/docs/index.rst
create mode 100644 docs/docs/install.rst
create mode 100644 docs/docs/quickstart.rst
create mode 100644 docs/docs/troubleshooting.rst
create mode 100644 docs/docs/usage.rst
create mode 100644 docs/index.html
create mode 100644 docs/news.rst
delete mode 100644 docs/uWSGI.md
diff --git a/Makefile b/Makefile
index 8921e15..96bb84e 100644
--- a/Makefile
+++ b/Makefile
@@ -8,3 +8,6 @@ js:
r.js -o isso/js/build.embed.js optimize="none" out="isso/js/embed.dev.js"
r.js -o isso/js/build.count.js
r.js -o isso/js/build.count.js optimize="none" out="isso/js/count.dev.js"
+site:
+ cd docs/ && sphinx-build -E -b dirhtml -a . _build
+
diff --git a/README.md b/README.md
index 0a6a05c..35001fa 100644
--- a/README.md
+++ b/README.md
@@ -92,66 +92,6 @@ This functionality is already included when you embed `embed.min.js`, do
### Client Configuration
-You can configure the client (the JS part) via `data-` attributes:
-
-* data-title
-
- When you start a new thread (= first comment on a page), Isso sends
- a GET request that page to see if it a) exists and b) parse the site's
- heading (currently used as subject in emails).
-
- Isso assumes that the title is inside an `h1` tag near the isso thread:
-
- ```html
-
-
- Website Title
-
-
- Post Title
-
- ...
- ```
-
- In this example, the detected title is `Post Title` as expected, but some
- older sites may only use a single `h1` as their website's maintitle, and
- a `h2` for the post title. Unfortunately this is unambiguous and you have
- to tell Isso what's the actual post title:
-
- ```html
-
- ```
-
- Make sure to escape the attribute value.
-
-* data-isso
-
- Isso usually detects the REST API automatically, but when you serve the JS
- script on a different location, this may fail. Use `data-isso` to
- override the API location:
-
- ```html
-
- ```
-
-* data-isso-css
-
- Set to `false` prevents Isso from automatically appending the stylesheet.
- Defaults to `true`.
-
- ```html
-
- ```
-
-* data-isso-lang
-
- Override useragent's preferred language. Currently available: german (de),
- english (en) and french (fr).
-
-* data-isso-reply-to-self
-
- Set to `true` when spam guard is configured with `reply-to-self = true`.
-
### Webserver configuration
* nginx configuration to run Isso on `/isso`:
diff --git a/docs/API.md b/docs/API.md
deleted file mode 100644
index 1d25a48..0000000
--- a/docs/API.md
+++ /dev/null
@@ -1,100 +0,0 @@
-Isso API
-========
-
-The Isso API uses HTTP and JSON as primary communication protocol.
-
-
-## JSON format
-
-When querying the API you either get an error, an object or list of objects
-representing the comment. Here's a example JSON returned from Isso:
-
-```json
-{
- "text": "Hello, World!",
- "author": "Bernd",
- "website": null,
- "votes": 0,
- "mode": 1,
- "id": 1,
- "parent": null,
- "hash": "68b329da9893e34099c7d8ad5cb9c940",
- "created": 1379001637.50,
- "modified": null
-}
-```
-
-text
-: required, comment as HTML
-
-author
-: author's name, may be `null`
-
-website
-: author's website, may be `null`
-
-votes
-: sum of up- and downvotes, defaults to zero.
-
-mode
-: * 1, accepted comment
- * 2, comment in moderation queue
- * 4, comment deleted, but is referenced
-
-id
-: unique comment number per thread
-
-parent
-: answer to a parent id, may be `null`
-
-hash
-: user identification, used to generate identicons
-
-created
-: time in seconds sinde epoch
-
-modified
-: last modification time in seconds, may be `null`
-
-
-## List comments
-
-List all visible comments for a thread. Does not include deleted and
-comments currently in moderation queue.
-
- GET /?uri=path
-
-You must encode `path`, e.g. to retrieve comments for `/hello-world/`:
-
- GET /?uri=%2Fhello-world%2F
-
-To disable automatic Markdown-to-HTML conversion, pass `plain=1` to the
-query URL:
-
- GET /?uri=...&plain=1
-
-As response, you either get 200, 400, or 404, which are pretty self-explanatory.
-
- GET /
- 400 BAD REQUEST
-
- GET /?uri=%2Fhello-world%2F
- 404 NOT FOUND
-
- GET /?uri=%2Fcomment-me%2F
- [{comment 1}, {comment 2}, ...]
-
-
-## Create comments
-
-...
-
-
-## Delete comments
-
-...
-
-
-## Up- and downvote comments
-
-...
diff --git a/docs/_isso/html5.py b/docs/_isso/html5.py
new file mode 100644
index 0000000..d6138ab
--- /dev/null
+++ b/docs/_isso/html5.py
@@ -0,0 +1,1298 @@
+#:Author: David Goodger (goodger@python.org)
+#:Contributor: Michael Beaumont (mjboamail@gmail.com)
+#:Copyright: This module has been placed in the public domain.
+
+
+try:
+ str = unicode
+except NameError:
+ pass
+
+import sys
+import os
+import os.path
+import re
+import urllib
+
+try: # check for the Python Imaging Library
+ import PIL.Image
+except ImportError:
+ try: # sometimes PIL modules are put in PYTHONPATH's root
+ import Image
+ class PIL(object): pass # dummy wrapper
+ PIL.Image = Image
+ except ImportError:
+ PIL = None
+
+from docutils import nodes, utils
+from docutils.utils.math import unichar2tex, pick_math_environment, math2html
+from docutils.utils.math.latex2mathml import parse_latex_math
+
+from sphinx.writers.html import HTMLTranslator
+
+
+class Isso(HTMLTranslator):
+
+ def __init__(self, builder, *args, **kwds):
+ HTMLTranslator.__init__(self, builder, *args, **kwds)
+ self.initial_header_level = 1
+
+ def starttag(self, node, tagname, suffix='\n', empty=False, **attributes):
+ """
+ Construct and return a start tag given a node (id & class attributes
+ are extracted), tag name, and optional attributes.
+ """
+ tagname = tagname.lower()
+ prefix = []
+ atts = {}
+ ids = []
+ for (name, value) in list(attributes.items()):
+ atts[name.lower()] = value
+ classes = []
+ languages = []
+ # unify class arguments and move language specification
+ for cls in node.get('classes', []) + atts.pop('class', '').split() :
+ if cls.startswith('language-'):
+ languages.append(cls[9:])
+ elif cls.strip() and cls not in classes:
+ classes.append(cls)
+ if languages:
+ # attribute name is 'lang' in XHTML 1.0 but 'xml:lang' in 1.1
+ atts[self.lang_attribute] = languages[0]
+ if classes:
+ atts['class'] = ' '.join(classes)
+ assert 'id' not in atts
+ ids.extend(node.get('ids', []))
+ if 'ids' in atts:
+ ids.extend(atts['ids'])
+ del atts['ids']
+ if ids:
+ atts['id'] = ids[0]
+ for id in ids[1:]:
+ # Add empty "span" elements for additional IDs. Note
+ # that we cannot use empty "a" elements because there
+ # may be targets inside of references, but nested "a"
+ # elements aren't allowed in XHTML (even if they do
+ # not all have a "href" attribute).
+ if empty:
+ # Empty tag. Insert target right in front of element.
+ prefix.append(' ' % id)
+ else:
+ # Non-empty tag. Place the auxiliary tag
+ # *inside* the element, as the first child.
+ suffix += ' ' % id
+ attlist = list(atts.items())
+ attlist.sort()
+ parts = [tagname]
+ for name, value in attlist:
+ # value=None was used for boolean attributes without
+ # value, but this isn't supported by XHTML.
+ assert value is not None
+ if isinstance(value, list):
+ values = [str(v) for v in value]
+ parts.append('%s="%s"' % (name.lower(),
+ self.attval(' '.join(values))))
+ else:
+ parts.append('%s="%s"' % (name.lower(),
+ self.attval(str(value))))
+ if empty:
+ infix = ' /'
+ else:
+ infix = ''
+ return ''.join(prefix) + '<%s%s>' % (' '.join(parts), infix) + suffix
+
+ def emptytag(self, node, tagname, suffix='\n', **attributes):
+ """Construct and return an XML-compatible empty tag."""
+ return self.starttag(node, tagname, suffix, empty=True, **attributes)
+
+ def set_class_on_child(self, node, class_, index=0):
+ """
+ Set class `class_` on the visible child no. index of `node`.
+ Do nothing if node has fewer children than `index`.
+ """
+ children = [n for n in node if not isinstance(n, nodes.Invisible)]
+ try:
+ child = children[index]
+ except IndexError:
+ return
+ child['classes'].append(class_)
+
+ def set_first_last(self, node):
+ self.set_class_on_child(node, 'first', 0)
+ self.set_class_on_child(node, 'last', -1)
+
+ def visit_Text(self, node):
+ text = node.astext()
+ encoded = self.encode(text)
+ if self.in_mailto and self.settings.cloak_email_addresses:
+ encoded = self.cloak_email(encoded)
+ self.body.append(encoded)
+
+ def depart_Text(self, node):
+ pass
+
+ def visit_abbreviation(self, node):
+ attrs = {}
+ if node.hasattr('explanation'):
+ attrs['title'] = node['explanation']
+ self.body.append(self.starttag(node, 'abbr', '', **attrs))
+
+ def depart_abbreviation(self, node):
+ self.body.append('')
+
+ def visit_acronym(self, node):
+ # @@@ implementation incomplete ("title" attribute)
+ self.body.append(self.starttag(node, 'acronym', ''))
+
+ def depart_acronym(self, node):
+ self.body.append('')
+
+ def visit_address(self, node):
+ self.visit_docinfo_item(node, 'address', meta=False)
+ self.body.append(self.starttag(node, 'pre', CLASS='address'))
+
+ def depart_address(self, node):
+ self.body.append('\n\n')
+ self.depart_docinfo_item()
+
+ def visit_admonition(self, node):
+ self.body.append(self.starttag(node, 'div'))
+ self.set_first_last(node)
+
+ def depart_admonition(self, node=None):
+ self.body.append('\n')
+
+ attribution_formats = {'dash': ('—', ''),
+ 'parentheses': ('(', ')'),
+ 'parens': ('(', ')'),
+ 'none': ('', '')}
+
+ def visit_attribution(self, node):
+ prefix, suffix = self.attribution_formats[self.settings.attribution]
+ self.context.append(suffix)
+ self.body.append(
+ self.starttag(node, 'p', prefix, CLASS='attribution'))
+
+ def depart_attribution(self, node):
+ self.body.append(self.context.pop() + '
\n')
+
+ def visit_author(self, node):
+ if isinstance(node.parent, nodes.authors):
+ if self.author_in_authors:
+ self.body.append('\n ')
+ else:
+ self.visit_docinfo_item(node, 'author')
+
+ def depart_author(self, node):
+ if isinstance(node.parent, nodes.authors):
+ self.author_in_authors = True
+ else:
+ self.depart_docinfo_item()
+
+ def visit_authors(self, node):
+ self.visit_docinfo_item(node, 'authors')
+ self.author_in_authors = False # initialize
+
+ def depart_authors(self, node):
+ self.depart_docinfo_item()
+
+ def visit_block_quote(self, node):
+ self.body.append(self.starttag(node, 'blockquote'))
+
+ def depart_block_quote(self, node):
+ self.body.append('\n')
+
+ def check_simple_list(self, node):
+ """Check for a simple list that can be rendered compactly."""
+ visitor = SimpleListChecker(self.document)
+ try:
+ node.walk(visitor)
+ except nodes.NodeFound:
+ return None
+ else:
+ return 1
+
+ def is_compactable(self, node):
+ return ('compact' in node['classes']
+ or (self.settings.compact_lists
+ and 'open' not in node['classes']
+ and (self.compact_simple
+ or self.topic_classes == ['contents']
+ or self.check_simple_list(node))))
+
+ def visit_bullet_list(self, node):
+ atts = {}
+ old_compact_simple = self.compact_simple
+ self.context.append((self.compact_simple, self.compact_p))
+ self.compact_p = None
+ self.compact_simple = self.is_compactable(node)
+ if self.compact_simple and not old_compact_simple:
+ atts['class'] = 'simple'
+ self.body.append(self.starttag(node, 'ul', **atts))
+
+ def depart_bullet_list(self, node):
+ self.compact_simple, self.compact_p = self.context.pop()
+ self.body.append('\n')
+
+ def visit_caption(self, node):
+ self.body.append(self.starttag(node, 'figcaption'))
+
+ def depart_caption(self, node):
+ self.body.append('\n')
+
+#changed
+ def visit_citation(self, node):
+ self.body.append(self.starttag(node, 'table',
+ CLASS='docutils citation',
+ frame="void", rules="none"))
+ self.body.append(' \n'
+ '\n'
+ '')
+ self.footnote_backrefs(node)
+
+ def depart_citation(self, node):
+ self.body.append(' \n'
+ ' \n\n')
+
+ def visit_citation_reference(self, node):
+ href = '#'
+ if 'refid' in node:
+ href += node['refid']
+ elif 'refname' in node:
+ href += self.document.nameids[node['refname']]
+ # else: # TODO system message (or already in the transform)?
+ # 'Citation reference missing.'
+ self.body.append(self.starttag(
+ node, 'a', '[', CLASS='citation-reference', href=href))
+
+ def depart_citation_reference(self, node):
+ self.body.append(']')
+
+ def visit_classifier(self, node):
+ self.body.append(' : ')
+ self.body.append(self.starttag(node, 'span', '', CLASS='classifier'))
+
+ def depart_classifier(self, node):
+ self.body.append('')
+
+ def visit_colspec(self, node):
+ self.colspecs.append(node)
+ # "stubs" list is an attribute of the tgroup element:
+ node.parent.stubs.append(node.attributes.get('stub'))
+
+ def depart_colspec(self, node):
+ pass
+
+ def write_colspecs(self):
+ width = 0
+ for node in self.colspecs:
+ width += node['colwidth']
+ for node in self.colspecs:
+ colwidth = int(node['colwidth'] * 100.0 / width + 0.5)
+ self.body.append(self.emptytag(node, 'col',
+ width='%i%%' % colwidth))
+ self.colspecs = []
+
+ def visit_comment(self, node,
+ sub=re.compile('-(?=-)').sub):
+ """Escape double-dashes in comment text."""
+ self.body.append('\n' % sub('- ', node.astext()))
+ # Content already processed:
+ raise nodes.SkipNode
+
+ def visit_compound(self, node):
+ self.body.append(self.starttag(node, 'div', CLASS='compound'))
+ if len(node) > 1:
+ node[0]['classes'].append('compound-first')
+ node[-1]['classes'].append('compound-last')
+ for child in node[1:-1]:
+ child['classes'].append('compound-middle')
+
+ def depart_compound(self, node):
+ self.body.append('\n')
+
+ def visit_container(self, node):
+ self.body.append(self.starttag(node, 'div', CLASS='container'))
+
+ def depart_container(self, node):
+ self.body.append('\n')
+
+ def visit_contact(self, node):
+ self.visit_docinfo_item(node, 'contact', meta=False)
+
+ def depart_contact(self, node):
+ self.depart_docinfo_item()
+
+ def visit_copyright(self, node):
+ self.visit_docinfo_item(node, 'copyright')
+
+ def depart_copyright(self, node):
+ self.depart_docinfo_item()
+
+ def visit_date(self, node):
+ self.visit_docinfo_item(node, 'date')
+
+ def depart_date(self, node):
+ self.depart_docinfo_item()
+
+ def visit_decoration(self, node):
+ pass
+
+ def depart_decoration(self, node):
+ pass
+
+ def visit_definition(self, node):
+ self.body.append('\n')
+ self.body.append(self.starttag(node, 'dd', ''))
+ self.set_first_last(node)
+
+ def depart_definition(self, node):
+ self.body.append('\n')
+
+ def visit_definition_list(self, node):
+ self.body.append(self.starttag(node, 'dl', CLASS='docutils'))
+
+ def depart_definition_list(self, node):
+ self.body.append('\n')
+
+ def visit_definition_list_item(self, node):
+ pass
+
+ def depart_definition_list_item(self, node):
+ pass
+
+ def visit_description(self, node):
+ self.body.append(self.starttag(node, 'td', ''))
+ self.set_first_last(node)
+
+ def depart_description(self, node):
+ self.body.append('')
+
+ def visit_docinfo(self, node):
+ self.context.append(len(self.body))
+ self.body.append(self.starttag(node, 'table',
+ CLASS='docinfo',
+ frame="void", rules="none"))
+ self.body.append(' \n'
+ ' \n'
+ '\n')
+ self.in_docinfo = True
+
+ def depart_docinfo(self, node):
+ self.body.append(' \n\n')
+ self.in_docinfo = False
+ start = self.context.pop()
+ self.docinfo = self.body[start:]
+ self.body = []
+
+ def visit_docinfo_item(self, node, name, meta=True):
+ if meta:
+ meta_tag = ' \n' \
+ % (name, self.attval(node.astext()))
+ self.add_meta(meta_tag)
+ self.body.append(self.starttag(node, 'tr', ''))
+ self.body.append('%s: \n'
+ % self.language.labels[name])
+ if len(node):
+ if isinstance(node[0], nodes.Element):
+ node[0]['classes'].append('first')
+ if isinstance(node[-1], nodes.Element):
+ node[-1]['classes'].append('last')
+
+ def depart_docinfo_item(self):
+ self.body.append(' \n')
+
+ def visit_doctest_block(self, node):
+ self.body.append(self.starttag(node, 'pre', CLASS='doctest-block'))
+
+ def depart_doctest_block(self, node):
+ self.body.append('\n\n')
+
+ def visit_document(self, node):
+ self.head.append('%s \n'
+ % self.encode(node.get('title', '')))
+
+ def depart_document(self, node):
+ self.head_prefix.extend([self.doctype,
+ self.head_prefix_template %
+ {'lang': self.settings.language_code}])
+ self.html_prolog.append(self.doctype)
+ self.meta.insert(0, self.content_type % self.settings.output_encoding)
+ self.head.insert(0, self.content_type % self.settings.output_encoding)
+ if self.math_header:
+ if self.math_output == 'mathjax':
+ self.head.extend(self.math_header)
+ else:
+ self.stylesheet.extend(self.math_header)
+ # skip content-type meta tag with interpolated charset value:
+ self.html_head.extend(self.head[1:])
+ self.body_prefix.append(self.starttag(node, 'div', CLASS='document'))
+ self.body_suffix.insert(0, '\n')
+ self.fragment.extend(self.body) # self.fragment is the "naked" body
+ self.html_body.extend(self.body_prefix[1:] + self.body_pre_docinfo
+ + self.docinfo + self.body
+ + self.body_suffix[:-1])
+ assert not self.context, 'len(context) = %s' % len(self.context)
+
+ def visit_emphasis(self, node):
+ self.body.append(self.starttag(node, 'em', ''))
+
+ def depart_emphasis(self, node):
+ self.body.append('')
+
+ def visit_entry(self, node):
+ atts = {'class': []}
+ if isinstance(node.parent.parent, nodes.thead):
+ atts['class'].append('head')
+ if node.parent.parent.parent.stubs[node.parent.column]:
+ # "stubs" list is an attribute of the tgroup element
+ atts['class'].append('stub')
+ if atts['class']:
+ tagname = 'th'
+ atts['class'] = ' '.join(atts['class'])
+ else:
+ tagname = 'td'
+ del atts['class']
+ node.parent.column += 1
+ if 'morerows' in node:
+ atts['rowspan'] = node['morerows'] + 1
+ if 'morecols' in node:
+ atts['colspan'] = node['morecols'] + 1
+ node.parent.column += node['morecols']
+ self.body.append(self.starttag(node, tagname, '', **atts))
+ self.context.append('%s>\n' % tagname.lower())
+ if len(node) == 0: # empty cell
+ self.body.append(' ')
+ self.set_first_last(node)
+
+ def depart_entry(self, node):
+ self.body.append(self.context.pop())
+
+ def visit_enumerated_list(self, node):
+ atts = {}
+ if 'start' in node:
+ atts['start'] = node['start']
+ if 'enumtype' in node:
+ atts['class'] = node['enumtype']
+ # @@@ To do: prefix, suffix. How? Change prefix/suffix to a
+ # single "format" attribute? Use CSS2?
+ old_compact_simple = self.compact_simple
+ self.context.append((self.compact_simple, self.compact_p))
+ self.compact_p = None
+ self.compact_simple = self.is_compactable(node)
+ if self.compact_simple and not old_compact_simple:
+ atts['class'] = (atts.get('class', '') + ' simple').strip()
+ self.body.append(self.starttag(node, 'ol', **atts))
+
+ def depart_enumerated_list(self, node):
+ self.compact_simple, self.compact_p = self.context.pop()
+ self.body.append('\n')
+
+ def visit_field(self, node):
+ self.body.append(self.starttag(node, 'tr', '', CLASS='field'))
+
+ def depart_field(self, node):
+ self.body.append('\n')
+
+ def visit_field_body(self, node):
+ self.body.append(self.starttag(node, 'td', '', CLASS='field-body'))
+ self.set_class_on_child(node, 'first', 0)
+ field = node.parent
+ if (self.compact_field_list or
+ isinstance(field.parent, nodes.docinfo) or
+ field.parent.index(field) == len(field.parent) - 1):
+ # If we are in a compact list, the docinfo, or if this is
+ # the last field of the field list, do not add vertical
+ # space after last element.
+ self.set_class_on_child(node, 'last', -1)
+
+ def depart_field_body(self, node):
+ self.body.append('\n')
+
+ def visit_field_list(self, node):
+ self.context.append((self.compact_field_list, self.compact_p))
+ self.compact_p = None
+ if 'compact' in node['classes']:
+ self.compact_field_list = True
+ elif (self.settings.compact_field_lists
+ and 'open' not in node['classes']):
+ self.compact_field_list = True
+ if self.compact_field_list:
+ for field in node:
+ field_body = field[-1]
+ assert isinstance(field_body, nodes.field_body)
+ children = [n for n in field_body
+ if not isinstance(n, nodes.Invisible)]
+ if not (len(children) == 0 or
+ len(children) == 1 and
+ isinstance(children[0],
+ (nodes.paragraph, nodes.line_block))):
+ self.compact_field_list = False
+ break
+ self.body.append(self.starttag(node, 'table', frame='void',
+ rules='none',
+ CLASS='docutils field-list'))
+ self.body.append(' \n'
+ ' \n'
+ '\n')
+
+ def depart_field_list(self, node):
+ self.body.append(' \n\n')
+ self.compact_field_list, self.compact_p = self.context.pop()
+
+ def visit_field_name(self, node):
+ atts = {}
+ if self.in_docinfo:
+ atts['class'] = 'docinfo-name'
+ else:
+ atts['class'] = 'field-name'
+ if ( self.settings.field_name_limit
+ and len(node.astext()) > self.settings.field_name_limit):
+ atts['colspan'] = 2
+ self.context.append('\n'
+ + self.starttag(node.parent, 'tr', '',
+ CLASS='field')
+ + ' ')
+ else:
+ self.context.append('')
+ self.body.append(self.starttag(node, 'th', '', **atts))
+
+ def depart_field_name(self, node):
+ self.body.append(':')
+ self.body.append(self.context.pop())
+
+ def visit_figure(self, node):
+ atts = {'class': 'figure'}
+ if node.get('width'):
+ atts['style'] = 'width: %s' % node['width']
+ if node.get('align'):
+ atts['class'] += " align-" + node['align']
+ self.body.append(self.starttag(node, 'figure', **atts))
+
+ def depart_figure(self, node):
+ self.body.append('\n')
+
+ def visit_footer(self, node):
+ self.context.append(len(self.body))
+
+ def depart_footer(self, node):
+ start = self.context.pop()
+ footer = [self.starttag(node, 'footer'),
+ '\n']
+ footer.extend(self.body[start:])
+ footer.append('\n\n')
+ self.footer.extend(footer)
+ self.body_suffix[:0] = footer
+ del self.body[start:]
+
+ def visit_footnote(self, node):
+ self.body.append(self.starttag(node, 'table',
+ CLASS='docutils footnote',
+ frame="void", rules="none"))
+ self.body.append(' \n'
+ '\n'
+ '')
+ self.footnote_backrefs(node)
+
+ def footnote_backrefs(self, node):
+ backlinks = []
+ backrefs = node['backrefs']
+ if self.settings.footnote_backlinks and backrefs:
+ if len(backrefs) == 1:
+ self.context.append('')
+ self.context.append('')
+ self.context.append(''
+ % backrefs[0])
+ else:
+ i = 1
+ for backref in backrefs:
+ backlinks.append(' %s '
+ % (backref, i))
+ i += 1
+ self.context.append('(%s) ' % ', '.join(backlinks))
+ self.context += ['', '']
+ else:
+ self.context.append('')
+ self.context += ['', '']
+ # If the node does not only consist of a label.
+ if len(node) > 1:
+ # If there are preceding backlinks, we do not set class
+ # 'first', because we need to retain the top-margin.
+ if not backlinks:
+ node[1]['classes'].append('first')
+ node[-1]['classes'].append('last')
+
+ def depart_footnote(self, node):
+ self.body.append(' \n'
+ ' \n\n')
+
+ def visit_footnote_reference(self, node):
+ href = '#' + node['refid']
+ format = self.settings.footnote_references
+ if format == 'brackets':
+ suffix = '['
+ self.context.append(']')
+ else:
+ assert format == 'superscript'
+ suffix = ''
+ self.context.append(' ')
+ self.body.append(self.starttag(node, 'a', suffix,
+ CLASS='footnote-reference', href=href))
+
+ def depart_footnote_reference(self, node):
+ self.body.append(self.context.pop() + '')
+
+ def visit_generated(self, node):
+ pass
+
+ def depart_generated(self, node):
+ pass
+
+ def visit_header(self, node):
+ self.context.append(len(self.body))
+
+ def depart_header(self, node):
+ start = self.context.pop()
+ header = [self.starttag(node, 'header')]
+ header.extend(self.body[start:])
+ header.append('\n\n\n')
+ self.body_prefix.extend(header)
+ self.header.extend(header)
+ del self.body[start:]
+
+ def visit_image(self, node):
+ atts = {}
+ uri = node['uri']
+ # place SVG and SWF images in an element
+ types = {'.svg': 'image/svg+xml',
+ '.swf': 'application/x-shockwave-flash'}
+ ext = os.path.splitext(uri)[1].lower()
+ if ext in ('.svg', '.swf'):
+ atts['data'] = uri
+ atts['type'] = types[ext]
+ else:
+ atts['src'] = uri
+ atts['alt'] = node.get('alt', uri)
+ # image size
+ if 'width' in node:
+ atts['width'] = node['width']
+ if 'height' in node:
+ atts['height'] = node['height']
+ if 'scale' in node:
+ if (PIL and not ('width' in node and 'height' in node)
+ and self.settings.file_insertion_enabled):
+ imagepath = urllib.url2pathname(uri)
+ try:
+ img = PIL.Image.open(
+ imagepath.encode(sys.getfilesystemencoding()))
+ except (IOError, UnicodeEncodeError):
+ pass # TODO: warn?
+ else:
+ self.settings.record_dependencies.add(
+ imagepath.replace('\\', '/'))
+ if 'width' not in atts:
+ atts['width'] = str(img.size[0])
+ if 'height' not in atts:
+ atts['height'] = str(img.size[1])
+ del img
+ for att_name in 'width', 'height':
+ if att_name in atts:
+ match = re.match(r'([0-9.]+)(\S*)$', atts[att_name])
+ assert match
+ atts[att_name] = '%s%s' % (
+ float(match.group(1)) * (float(node['scale']) / 100),
+ match.group(2))
+ style = []
+ for att_name in 'width', 'height':
+ if att_name in atts:
+ if re.match(r'^[0-9.]+$', atts[att_name]):
+ # Interpret unitless values as pixels.
+ atts[att_name] += 'px'
+ style.append('%s: %s;' % (att_name, atts[att_name]))
+ del atts[att_name]
+ if style:
+ atts['style'] = ' '.join(style)
+ if (isinstance(node.parent, nodes.TextElement) or
+ (isinstance(node.parent, nodes.reference) and
+ not isinstance(node.parent.parent, nodes.TextElement))):
+ # Inline context or surrounded by ... .
+ suffix = ''
+ else:
+ suffix = '\n'
+ if 'align' in node:
+ atts['class'] = 'align-%s' % node['align']
+ self.context.append('')
+ if ext in ('.svg', '.swf'): # place in an object element,
+ # do NOT use an empty tag: incorrect rendering in browsers
+ self.body.append(self.starttag(node, 'object', suffix, **atts) +
+ node.get('alt', uri) + ' ' + suffix)
+ else:
+ self.body.append(self.emptytag(node, 'img', suffix, **atts))
+
+ def depart_image(self, node):
+ self.body.append(self.context.pop())
+
+ def visit_inline(self, node):
+ self.body.append(self.starttag(node, 'span', ''))
+
+ def depart_inline(self, node):
+ self.body.append('')
+
+ def visit_label(self, node):
+ # Context added in footnote_backrefs.
+ self.body.append(self.starttag(node, 'td', '%s[' % self.context.pop(),
+ CLASS='label'))
+
+ def depart_label(self, node):
+ # Context added in footnote_backrefs.
+ self.body.append(']%s%s' % (self.context.pop(), self.context.pop()))
+
+ def visit_legend(self, node):
+ self.body.append(self.starttag(node, 'div', CLASS='legend'))
+
+ def depart_legend(self, node):
+ self.body.append('\n')
+
+ def visit_line(self, node):
+ self.body.append(self.starttag(node, 'div', suffix='', CLASS='line'))
+ if not len(node):
+ self.body.append(' ')
+
+ def depart_line(self, node):
+ self.body.append('\n')
+
+ def visit_line_block(self, node):
+ self.body.append(self.starttag(node, 'div', CLASS='line-block'))
+
+ def depart_line_block(self, node):
+ self.body.append('\n')
+
+ def visit_list_item(self, node):
+ self.body.append(self.starttag(node, 'li', ''))
+ if len(node):
+ node[0]['classes'].append('first')
+
+ def visit_math(self, node, math_env=''):
+ # If the method is called from visit_math_block(), math_env != ''.
+
+ # As there is no native HTML math support, we provide alternatives:
+ # LaTeX and MathJax math_output modes simply wrap the content,
+ # HTML and MathML math_output modes also convert the math_code.
+ if self.math_output not in ('mathml', 'html', 'mathjax', 'latex'):
+ self.document.reporter.error(
+ 'math-output format "%s" not supported '
+ 'falling back to "latex"'% self.math_output)
+ self.math_output = 'latex'
+ #
+ # HTML container
+ tags = {# math_output: (block, inline, class-arguments)
+ 'mathml': ('div', '', ''),
+ 'html': ('div', 'span', 'formula'),
+ 'mathjax': ('div', 'span', 'math'),
+ 'latex': ('pre', 'tt', 'math'),
+ }
+ tag = tags[self.math_output][math_env == '']
+ clsarg = tags[self.math_output][2]
+ # LaTeX container
+ wrappers = {# math_mode: (inline, block)
+ 'mathml': (None, None),
+ 'html': ('$%s$', u'\\begin{%s}\n%s\n\\end{%s}'),
+ 'mathjax': ('\(%s\)', u'\\begin{%s}\n%s\n\\end{%s}'),
+ 'latex': (None, None),
+ }
+ wrapper = wrappers[self.math_output][math_env != '']
+ # get and wrap content
+ math_code = node.astext().translate(unichar2tex.uni2tex_table)
+ if wrapper and math_env:
+ math_code = wrapper % (math_env, math_code, math_env)
+ elif wrapper:
+ math_code = wrapper % math_code
+ # settings and conversion
+ if self.math_output in ('latex', 'mathjax'):
+ math_code = self.encode(math_code)
+ if self.math_output == 'mathjax' and not self.math_header:
+ if self.math_output_options:
+ self.mathjax_url = self.math_output_options[0]
+ self.math_header = [self.mathjax_script % self.mathjax_url]
+ elif self.math_output == 'html':
+ if self.math_output_options and not self.math_header:
+ self.math_header = [self.stylesheet_call(
+ utils.find_file_in_dirs(s, self.settings.stylesheet_dirs))
+ for s in self.math_output_options[0].split(',')]
+ # TODO: fix display mode in matrices and fractions
+ math2html.DocumentParameters.displaymode = (math_env != '')
+ math_code = math2html.math2html(math_code)
+ elif self.math_output == 'mathml':
+ self.doctype = self.doctype_mathml
+ self.content_type = self.content_type_mathml
+ try:
+ mathml_tree = parse_latex_math(math_code, inline=not(math_env))
+ math_code = ''.join(mathml_tree.xml())
+ except SyntaxError as err:
+ err_node = self.document.reporter.error(err, base_node=node)
+ self.visit_system_message(err_node)
+ self.body.append(self.starttag(node, 'p'))
+ self.body.append(u','.join(err.args))
+ self.body.append('\n')
+ self.body.append(self.starttag(node, 'pre',
+ CLASS='literal-block'))
+ self.body.append(self.encode(math_code))
+ self.body.append('\n\n')
+ self.depart_system_message(err_node)
+ raise nodes.SkipNode
+ # append to document body
+ if tag:
+ self.body.append(self.starttag(node, tag,
+ suffix='\n'*bool(math_env),
+ CLASS=clsarg))
+ self.body.append(math_code)
+ if math_env:
+ self.body.append('\n')
+ if tag:
+ self.body.append('%s>\n' % tag)
+ # Content already processed:
+ raise nodes.SkipNode
+
+ def depart_math(self, node):
+ pass # never reached
+
+ def visit_math_block(self, node):
+ # print node.astext().encode('utf8')
+ math_env = pick_math_environment(node.astext())
+ self.visit_math(node, math_env=math_env)
+
+ def depart_math_block(self, node):
+ pass # never reached
+
+ def visit_meta(self, node):
+ meta = self.emptytag(node, 'meta', **node.non_default_attributes())
+ self.add_meta(meta)
+
+ def depart_meta(self, node):
+ pass
+
+ def add_meta(self, tag):
+ self.meta.append(tag)
+ self.head.append(tag)
+
+ def visit_option(self, node):
+ if self.context[-1]:
+ self.body.append(', ')
+ self.body.append(self.starttag(node, 'span', '', CLASS='option'))
+
+ def depart_option(self, node):
+ self.body.append('')
+ self.context[-1] += 1
+
+ def visit_option_argument(self, node):
+ self.body.append(node.get('delimiter', ' '))
+ self.body.append(self.starttag(node, 'var', ''))
+
+ def depart_option_argument(self, node):
+ self.body.append('')
+
+ def visit_option_group(self, node):
+ atts = {}
+ if ( self.settings.option_limit
+ and len(node.astext()) > self.settings.option_limit):
+ atts['colspan'] = 2
+ self.context.append('\n ')
+ else:
+ self.context.append('')
+ self.body.append(
+ self.starttag(node, 'td', CLASS='option-group', **atts))
+ self.body.append('')
+ self.context.append(0) # count number of options
+
+ def depart_option_group(self, node):
+ self.context.pop()
+ self.body.append(' \n')
+ self.body.append(self.context.pop())
+
+ def visit_option_list(self, node):
+ self.body.append(
+ self.starttag(node, 'table', CLASS='docutils option-list',
+ frame="void", rules="none"))
+ self.body.append(' \n'
+ ' \n'
+ '\n')
+
+ def depart_option_list(self, node):
+ self.body.append(' \n\n')
+
+ def visit_option_list_item(self, node):
+ self.body.append(self.starttag(node, 'tr', ''))
+
+ def depart_option_list_item(self, node):
+ self.body.append('\n')
+
+ def visit_option_string(self, node):
+ pass
+
+ def depart_option_string(self, node):
+ pass
+
+ def visit_organization(self, node):
+ self.visit_docinfo_item(node, 'organization')
+
+ def depart_organization(self, node):
+ self.depart_docinfo_item()
+
+ def should_be_compact_paragraph(self, node):
+ """
+ Determine if the tags around paragraph ``node`` can be omitted.
+ """
+ if (isinstance(node.parent, nodes.document) or
+ isinstance(node.parent, nodes.compound)):
+ # Never compact paragraphs in document or compound.
+ return False
+ for key, value in node.attlist():
+ if (node.is_not_default(key) and
+ not (key == 'classes' and value in
+ ([], ['first'], ['last'], ['first', 'last']))):
+ # Attribute which needs to survive.
+ return False
+ first = isinstance(node.parent[0], nodes.label) # skip label
+ for child in node.parent.children[first:]:
+ # only first paragraph can be compact
+ if isinstance(child, nodes.Invisible):
+ continue
+ if child is node:
+ break
+ return False
+ parent_length = len([n for n in node.parent if not isinstance(
+ n, (nodes.Invisible, nodes.label))])
+ if ( self.compact_simple
+ or self.compact_field_list
+ or self.compact_p and parent_length == 1):
+ return True
+ return False
+
+ def visit_paragraph(self, node):
+ if self.should_be_compact_paragraph(node):
+ self.context.append('')
+ else:
+ self.body.append(self.starttag(node, 'p', ''))
+ self.context.append('
\n')
+
+ def depart_paragraph(self, node):
+ self.body.append(self.context.pop())
+
+ def visit_problematic(self, node):
+ if node.hasattr('refid'):
+ self.body.append('' % node['refid'])
+ self.context.append(' ')
+ else:
+ self.context.append('')
+ self.body.append(self.starttag(node, 'span', '', CLASS='problematic'))
+
+ def depart_problematic(self, node):
+ self.body.append('')
+ self.body.append(self.context.pop())
+
+ def visit_raw(self, node):
+ if 'html' in node.get('format', '').split():
+ t = isinstance(node.parent, nodes.TextElement) and 'span' or 'div'
+ if node['classes']:
+ self.body.append(self.starttag(node, t, suffix=''))
+ self.body.append(node.astext())
+ if node['classes']:
+ self.body.append('%s>' % t)
+ # Keep non-HTML raw text out of output:
+ raise nodes.SkipNode
+
+ def visit_reference(self, node):
+ atts = {'class': 'reference'}
+ if 'refuri' in node:
+ atts['href'] = node['refuri']
+ if ( self.settings.cloak_email_addresses
+ and atts['href'].startswith('mailto:')):
+ atts['href'] = self.cloak_mailto(atts['href'])
+ self.in_mailto = True
+ atts['class'] += ' external'
+ else:
+ assert 'refid' in node, \
+ 'References must have "refuri" or "refid" attribute.'
+ atts['href'] = '#' + node['refid']
+ atts['class'] += ' internal'
+ if not isinstance(node.parent, nodes.TextElement):
+ assert len(node) == 1 and isinstance(node[0], nodes.image)
+ atts['class'] += ' image-reference'
+ self.body.append(self.starttag(node, 'a', '', **atts))
+
+ def depart_reference(self, node):
+ self.body.append('')
+ if not isinstance(node.parent, nodes.TextElement):
+ self.body.append('\n')
+ self.in_mailto = False
+
+ def visit_revision(self, node):
+ self.visit_docinfo_item(node, 'revision', meta=False)
+
+ def depart_revision(self, node):
+ self.depart_docinfo_item()
+
+ def visit_row(self, node):
+ self.body.append(self.starttag(node, 'tr', ''))
+ node.column = 0
+
+ def depart_row(self, node):
+ self.body.append('\n')
+
+ def visit_rubric(self, node):
+ self.body.append(self.starttag(node, 'p', '', CLASS='rubric'))
+
+ def depart_rubric(self, node):
+ self.body.append('\n')
+
+ def visit_section(self, node):
+ self.section_level += 1
+ self.body.append(self.starttag(node, 'section'))
+
+ def depart_section(self, node):
+ self.section_level -= 1
+ self.body.append('\n')
+
+ def visit_sidebar(self, node):
+ self.body.append(
+ self.starttag(node, 'aside'))
+ self.set_first_last(node)
+ self.in_sidebar = True
+
+ def depart_sidebar(self, node):
+ self.body.append('\n')
+ self.in_sidebar = False
+
+ def visit_status(self, node):
+ self.visit_docinfo_item(node, 'status', meta=False)
+
+ def depart_status(self, node):
+ self.depart_docinfo_item()
+
+ def visit_strong(self, node):
+ self.body.append(self.starttag(node, 'strong', ''))
+
+ def depart_strong(self, node):
+ self.body.append('')
+
+ def visit_subscript(self, node):
+ self.body.append(self.starttag(node, 'sub', ''))
+
+ def depart_subscript(self, node):
+ self.body.append('')
+
+ def visit_substitution_definition(self, node):
+ """Internal only."""
+ raise nodes.SkipNode
+
+ def visit_substitution_reference(self, node):
+ self.unimplemented_visit(node)
+
+ def visit_subtitle(self, node):
+ if isinstance(node.parent, nodes.sidebar):
+ self.body.append(self.starttag(node, 'p', '',
+ CLASS='sidebar-subtitle'))
+ self.context.append('\n')
+ elif isinstance(node.parent, nodes.document):
+ self.body.append(self.starttag(node, 'h2', '', CLASS='subtitle'))
+ self.context.append('\n')
+ self.in_document_title = len(self.body)
+ elif isinstance(node.parent, nodes.section):
+ tag = 'h%s' % (self.section_level + self.initial_header_level - 1)
+ self.body.append(
+ self.starttag(node, tag, '', CLASS='section-subtitle') +
+ self.starttag({}, 'span', '', CLASS='section-subtitle'))
+ self.context.append('%s>\n' % tag)
+
+ def depart_subtitle(self, node):
+ self.body.append(self.context.pop())
+ if self.in_document_title:
+ self.subtitle = self.body[self.in_document_title:-1]
+ self.in_document_title = 0
+ self.body_pre_docinfo.extend(self.body)
+ self.html_subtitle.extend(self.body)
+ del self.body[:]
+
+ def visit_superscript(self, node):
+ self.body.append(self.starttag(node, 'sup', ''))
+
+ def depart_superscript(self, node):
+ self.body.append('')
+
+ def visit_system_message(self, node):
+ self.body.append(self.starttag(node, 'div', CLASS='system-message'))
+ self.body.append('')
+ backref_text = ''
+ if len(node['backrefs']):
+ backrefs = node['backrefs']
+ if len(backrefs) == 1:
+ backref_text = ('; backlink '
+ % backrefs[0])
+ else:
+ i = 1
+ backlinks = []
+ for backref in backrefs:
+ backlinks.append('%s ' % (backref, i))
+ i += 1
+ backref_text = ('; backlinks: %s '
+ % ', '.join(backlinks))
+ if node.hasattr('line'):
+ line = ', line %s' % node['line']
+ else:
+ line = ''
+ self.body.append('System Message: %s/%s '
+ '(%s %s)%s
\n'
+ % (node['type'], node['level'],
+ self.encode(node['source']), line, backref_text))
+
+ def depart_system_message(self, node):
+ self.body.append('\n')
+
+ def visit_table(self, node):
+ self.context.append(self.compact_p)
+ self.compact_p = True
+ classes = ' '.join(['docutils', self.settings.table_style]).strip()
+ self.body.append(
+ self.starttag(node, 'table', CLASS=classes, border="1"))
+
+ def depart_table(self, node):
+ self.compact_p = self.context.pop()
+ self.body.append('\n')
+
+ def visit_target(self, node):
+ if not ('refuri' in node or 'refid' in node
+ or 'refname' in node):
+ self.body.append(self.starttag(node, 'span', '', CLASS='target'))
+ self.context.append('')
+ else:
+ self.context.append('')
+
+ def depart_target(self, node):
+ self.body.append(self.context.pop())
+
+ def visit_tbody(self, node):
+ self.write_colspecs()
+ self.body.append(self.context.pop()) # '\n' or ''
+ self.body.append(self.starttag(node, 'tbody', valign='top'))
+
+ def depart_tbody(self, node):
+ self.body.append('\n')
+
+ def visit_term(self, node):
+ self.body.append(self.starttag(node, 'dt', ''))
+
+ def depart_term(self, node):
+ """
+ Leave the end tag to `self.visit_definition()`, in case there's a
+ classifier.
+ """
+ pass
+
+ def visit_tgroup(self, node):
+ # Mozilla needs :
+ self.body.append(self.starttag(node, 'colgroup'))
+ # Appended by thead or tbody:
+ self.context.append(' \n')
+ node.stubs = []
+
+ def depart_tgroup(self, node):
+ pass
+
+ def visit_thead(self, node):
+ self.write_colspecs()
+ self.body.append(self.context.pop()) # '\n'
+ # There may or may not be a ; this is for to use:
+ self.context.append('')
+ self.body.append(self.starttag(node, 'thead', valign='bottom'))
+
+ def depart_thead(self, node):
+ self.body.append('\n')
+
+ def visit_title(self, node):
+ if self.section_level == 1:
+ raise nodes.SkipNode
+ HTMLTranslator.visit_title(self, node)
+
+ def depart_title(self, node):
+ self.body.append(self.context.pop())
+ if self.in_document_title:
+ self.title = self.body[self.in_document_title:-1]
+ self.in_document_title = 0
+ self.body_pre_docinfo.extend(self.body)
+ self.html_title.extend(self.body)
+ del self.body[:]
+
+ def visit_title_reference(self, node):
+ self.body.append(self.starttag(node, 'cite', ''))
+
+ def depart_title_reference(self, node):
+ self.body.append('')
+
+ def visit_topic(self, node):
+ self.body.append(self.starttag(node, 'section', CLASS='topic'))
+ self.topic_classes = node['classes']
+
+ def depart_topic(self, node):
+ self.body.append('\n')
+ self.topic_classes = []
+
+ def visit_transition(self, node):
+ self.body.append(self.emptytag(node, 'hr', CLASS='docutils'))
+
+ def depart_transition(self, node):
+ pass
+
+ def visit_version(self, node):
+ self.visit_docinfo_item(node, 'version', meta=False)
+
+ def depart_version(self, node):
+ self.depart_docinfo_item()
+
+ def unimplemented_visit(self, node):
+ raise NotImplementedError('visiting unimplemented node type: %s'
+ % node.__class__.__name__)
+
+
+class SimpleListChecker(nodes.GenericNodeVisitor):
+
+ """
+ Raise `nodes.NodeFound` if non-simple list item is encountered.
+
+ Here "simple" means a list item containing nothing other than a single
+ paragraph, a simple list, or a paragraph followed by a simple list.
+ """
+
+ def default_visit(self, node):
+ raise nodes.NodeFound
+
+ def visit_bullet_list(self, node):
+ pass
+
+ def visit_enumerated_list(self, node):
+ pass
+
+ def visit_list_item(self, node):
+ children = []
+ for child in node.children:
+ if not isinstance(child, nodes.Invisible):
+ children.append(child)
+ if (children and isinstance(children[0], nodes.paragraph)
+ and (isinstance(children[-1], nodes.bullet_list)
+ or isinstance(children[-1], nodes.enumerated_list))):
+ children.pop()
+ if len(children) <= 1:
+ return
+ else:
+ raise nodes.NodeFound
+
+ def visit_paragraph(self, node):
+ raise nodes.SkipNode
+
+ def invisible_visit(self, node):
+ """Invisible nodes should be ignored."""
+ raise nodes.SkipNode
+
+ visit_comment = invisible_visit
+ visit_substitution_definition = invisible_visit
+ visit_target = invisible_visit
+ visit_pending = invisible_visit
diff --git a/docs/_isso/layout.html b/docs/_isso/layout.html
new file mode 100644
index 0000000..70c11ab
--- /dev/null
+++ b/docs/_isso/layout.html
@@ -0,0 +1,123 @@
+
+{%- set url_root = pathto('', 1) %}
+{# XXX necessary? #}
+{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
+
+{%- macro script() %}
+
+ {%- for scriptfile in script_files %}
+
+ {%- endfor %}
+{%- endmacro %}
+
+{%- macro css() %}
+
+
+
+ {%- for cssfile in css_files %}
+
+ {%- endfor %}
+{%- endmacro %}
+
+
+
+
+ {{ metatags }}
+ {%- block htmltitle %}
+ {{ title|striptags|e }}
+ {%- endblock %}
+ {{ css() }}
+ {%- if not embedded %}
+ {{ script() }}
+ {%- if use_opensearch %}
+
+ {%- endif %}
+ {%- if favicon %}
+
+ {%- endif %}
+ {%- endif %}
+{%- block linktags %}
+ {%- if hasdoc('about') %}
+
+ {%- endif %}
+ {%- if hasdoc('genindex') %}
+
+ {%- endif %}
+ {%- if hasdoc('search') %}
+
+ {%- endif %}
+ {%- if hasdoc('copyright') %}
+
+ {%- endif %}
+
+ {%- if parents %}
+
+ {%- endif %}
+ {%- if next %}
+
+ {%- endif %}
+ {%- if prev %}
+
+ {%- endif %}
+{%- endblock %}
+{%- block extrahead %} {% endblock %}
+
+
+
+
+
+
+ {% block header %} {% endblock %}
+
+
+
+
+ {% block body %} {% endblock %}
+
+
+
+
+
+
+
+{%- endblock %}
+
+
diff --git a/docs/_isso/page.html b/docs/_isso/page.html
new file mode 100644
index 0000000..593c09a
--- /dev/null
+++ b/docs/_isso/page.html
@@ -0,0 +1,16 @@
+{%- extends "layout.html" %}
+{% block header %}
+
+{% endblock %}
+{% block body %}
+
+
+ {{ body }}
+
+{% endblock %}
diff --git a/docs/_isso/search.html b/docs/_isso/search.html
new file mode 100644
index 0000000..1cd9cc8
--- /dev/null
+++ b/docs/_isso/search.html
@@ -0,0 +1,54 @@
+{% extends "layout.html" %}
+{% block header %}
+
+{% endblock %}
+{% set title = _('Search') %}
+{% set script_files = script_files + ['_static/searchtools.js'] %}
+{% block extrahead %}
+
+ {# this is used when loading the search index using $.ajax fails,
+ such as on Chrome for documents on localhost #}
+
+ {{ super() }}
+{% endblock %}
+{% block body %}
+
+
+
+ {% trans %}Please activate JavaScript to enable the search
+ functionality.{% endtrans %}
+
+
+
+ {% trans %}From here you can search these documents. Enter your search
+ words into the box below and click "search". Note that the search
+ function will automatically search for all of the words. Pages
+ containing fewer words won't appear in the result list.{% endtrans %}
+
+
+ {% if search_performed %}
+ {{ _('Search Results') }}
+ {% if not search_results %}
+ {{ _("Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.") }}
+ {% endif %}
+ {% endif %}
+
+ {% if search_results %}
+
+ {% for href, caption, context in search_results %}
+ {{ caption }}
+ {{ context|e }}
+
+ {% endfor %}
+
+ {% endif %}
+
+{% endblock %}
diff --git a/docs/_isso/searchbox.html b/docs/_isso/searchbox.html
new file mode 100644
index 0000000..1426c34
--- /dev/null
+++ b/docs/_isso/searchbox.html
@@ -0,0 +1,12 @@
+{%- if pagename != "search" %}
+
+
{{ _('Quick search') }}
+
+
+
+{%- endif %}
diff --git a/docs/_isso/sidebar-docs.html b/docs/_isso/sidebar-docs.html
new file mode 100644
index 0000000..a9a7dde
--- /dev/null
+++ b/docs/_isso/sidebar-docs.html
@@ -0,0 +1,27 @@
+{% macro doc(path, title) %}
+ {%- if pagename == path -%}
+
+ {% else %}
+
+ {%- endif -%}
+ {{ title }}
+{% endmacro %}
+
+Getting Started
+
+ {{ doc("docs/install", "Installation") }}
+ {{ doc("docs/quickstart", "Quickstart") }}
+ {{ doc("docs/troubleshooting", "Troubleshooting") }}
+
+Configuration
+
+ {{ doc("docs/configuration/setup", "Setup") }}
+ {{ doc("docs/configuration/server", "Server") }}
+ {{ doc("docs/configuration/client", "Client") }}
+
+Advanced
+
+ {{ doc("docs/extras/multi-site", "Multiple Sites") }}
+ {{ doc("docs/extras/uwsgi", "uWSGI") }}
+ {{ doc("docs/extras/api", "API") }}
+
diff --git a/docs/_isso/theme.conf b/docs/_isso/theme.conf
new file mode 100644
index 0000000..ccf3c9b
--- /dev/null
+++ b/docs/_isso/theme.conf
@@ -0,0 +1,3 @@
+[theme]
+inherit = basic
+stylesheet = css/site.css
diff --git a/docs/_static/css/bourbon/_bourbon-deprecated-upcoming.scss b/docs/_static/css/bourbon/_bourbon-deprecated-upcoming.scss
new file mode 100644
index 0000000..5332496
--- /dev/null
+++ b/docs/_static/css/bourbon/_bourbon-deprecated-upcoming.scss
@@ -0,0 +1,13 @@
+//************************************************************************//
+// These mixins/functions are deprecated
+// They will be removed in the next MAJOR version release
+//************************************************************************//
+@mixin box-shadow ($shadows...) {
+ @include prefixer(box-shadow, $shadows, spec);
+ @warn "box-shadow is deprecated and will be removed in the next major version release";
+}
+
+@mixin background-size ($lengths...) {
+ @include prefixer(background-size, $lengths, spec);
+ @warn "background-size is deprecated and will be removed in the next major version release";
+}
diff --git a/docs/_static/css/bourbon/_bourbon.scss b/docs/_static/css/bourbon/_bourbon.scss
new file mode 100644
index 0000000..53fbca8
--- /dev/null
+++ b/docs/_static/css/bourbon/_bourbon.scss
@@ -0,0 +1,59 @@
+// Custom Helpers
+@import "helpers/deprecated-webkit-gradient";
+@import "helpers/gradient-positions-parser";
+@import "helpers/linear-positions-parser";
+@import "helpers/radial-arg-parser";
+@import "helpers/radial-positions-parser";
+@import "helpers/render-gradients";
+@import "helpers/shape-size-stripper";
+
+// Custom Functions
+@import "functions/compact";
+@import "functions/flex-grid";
+@import "functions/grid-width";
+@import "functions/linear-gradient";
+@import "functions/modular-scale";
+@import "functions/px-to-em";
+@import "functions/radial-gradient";
+@import "functions/tint-shade";
+@import "functions/transition-property-name";
+
+// CSS3 Mixins
+@import "css3/animation";
+@import "css3/appearance";
+@import "css3/backface-visibility";
+@import "css3/background";
+@import "css3/background-image";
+@import "css3/border-image";
+@import "css3/border-radius";
+@import "css3/box-sizing";
+@import "css3/columns";
+@import "css3/flex-box";
+@import "css3/font-face";
+@import "css3/hidpi-media-query";
+@import "css3/image-rendering";
+@import "css3/inline-block";
+@import "css3/keyframes";
+@import "css3/linear-gradient";
+@import "css3/perspective";
+@import "css3/radial-gradient";
+@import "css3/transform";
+@import "css3/transition";
+@import "css3/user-select";
+@import "css3/placeholder";
+
+// Addons & other mixins
+@import "addons/button";
+@import "addons/clearfix";
+@import "addons/font-family";
+@import "addons/hide-text";
+@import "addons/html5-input-types";
+@import "addons/position";
+@import "addons/prefixer";
+@import "addons/retina-image";
+@import "addons/size";
+@import "addons/timing-functions";
+@import "addons/triangle";
+
+// Soon to be deprecated Mixins
+@import "bourbon-deprecated-upcoming";
diff --git a/docs/_static/css/bourbon/addons/_button.scss b/docs/_static/css/bourbon/addons/_button.scss
new file mode 100644
index 0000000..3ae393c
--- /dev/null
+++ b/docs/_static/css/bourbon/addons/_button.scss
@@ -0,0 +1,273 @@
+@mixin button ($style: simple, $base-color: #4294f0) {
+
+ @if type-of($style) == color {
+ $base-color: $style;
+ $style: simple;
+ }
+
+ // Grayscale button
+ @if $base-color == grayscale($base-color) {
+ @if $style == simple {
+ @include simple($base-color, $grayscale: true);
+ }
+
+ @else if $style == shiny {
+ @include shiny($base-color, $grayscale: true);
+ }
+
+ @else if $style == pill {
+ @include pill($base-color, $grayscale: true);
+ }
+ }
+
+ // Colored button
+ @else {
+ @if $style == simple {
+ @include simple($base-color);
+ }
+
+ @else if $style == shiny {
+ @include shiny($base-color);
+ }
+
+ @else if $style == pill {
+ @include pill($base-color);
+ }
+ }
+
+ &:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+}
+
+
+// Simple Button
+//************************************************************************//
+@mixin simple($base-color, $grayscale: false) {
+ $color: hsl(0, 0, 100%);
+ $border: adjust-color($base-color, $saturation: 9%, $lightness: -14%);
+ $inset-shadow: adjust-color($base-color, $saturation: -8%, $lightness: 15%);
+ $stop-gradient: adjust-color($base-color, $saturation: 9%, $lightness: -11%);
+ $text-shadow: adjust-color($base-color, $saturation: 15%, $lightness: -18%);
+
+ @if lightness($base-color) > 70% {
+ $color: hsl(0, 0, 20%);
+ $text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%);
+ }
+
+ @if $grayscale == true {
+ $border: grayscale($border);
+ $inset-shadow: grayscale($inset-shadow);
+ $stop-gradient: grayscale($stop-gradient);
+ $text-shadow: grayscale($text-shadow);
+ }
+
+ border: 1px solid $border;
+ border-radius: 3px;
+ box-shadow: inset 0 1px 0 0 $inset-shadow;
+ color: $color;
+ display: inline-block;
+ font-size: 11px;
+ font-weight: bold;
+ @include linear-gradient ($base-color, $stop-gradient);
+ padding: 7px 18px;
+ text-decoration: none;
+ text-shadow: 0 1px 0 $text-shadow;
+ background-clip: padding-box;
+
+ &:hover:not(:disabled) {
+ $base-color-hover: adjust-color($base-color, $saturation: -4%, $lightness: -5%);
+ $inset-shadow-hover: adjust-color($base-color, $saturation: -7%, $lightness: 5%);
+ $stop-gradient-hover: adjust-color($base-color, $saturation: 8%, $lightness: -14%);
+
+ @if $grayscale == true {
+ $base-color-hover: grayscale($base-color-hover);
+ $inset-shadow-hover: grayscale($inset-shadow-hover);
+ $stop-gradient-hover: grayscale($stop-gradient-hover);
+ }
+
+ box-shadow: inset 0 1px 0 0 $inset-shadow-hover;
+ cursor: pointer;
+ @include linear-gradient ($base-color-hover, $stop-gradient-hover);
+ }
+
+ &:active:not(:disabled) {
+ $border-active: adjust-color($base-color, $saturation: 9%, $lightness: -14%);
+ $inset-shadow-active: adjust-color($base-color, $saturation: 7%, $lightness: -17%);
+
+ @if $grayscale == true {
+ $border-active: grayscale($border-active);
+ $inset-shadow-active: grayscale($inset-shadow-active);
+ }
+
+ border: 1px solid $border-active;
+ box-shadow: inset 0 0 8px 4px $inset-shadow-active, inset 0 0 8px 4px $inset-shadow-active, 0 1px 1px 0 #eee;
+ }
+}
+
+
+// Shiny Button
+//************************************************************************//
+@mixin shiny($base-color, $grayscale: false) {
+ $color: hsl(0, 0, 100%);
+ $border: adjust-color($base-color, $red: -117, $green: -111, $blue: -81);
+ $border-bottom: adjust-color($base-color, $red: -126, $green: -127, $blue: -122);
+ $fourth-stop: adjust-color($base-color, $red: -79, $green: -70, $blue: -46);
+ $inset-shadow: adjust-color($base-color, $red: 37, $green: 29, $blue: 12);
+ $second-stop: adjust-color($base-color, $red: -56, $green: -50, $blue: -33);
+ $text-shadow: adjust-color($base-color, $red: -140, $green: -141, $blue: -114);
+ $third-stop: adjust-color($base-color, $red: -86, $green: -75, $blue: -48);
+
+ @if lightness($base-color) > 70% {
+ $color: hsl(0, 0, 20%);
+ $text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%);
+ }
+
+ @if $grayscale == true {
+ $border: grayscale($border);
+ $border-bottom: grayscale($border-bottom);
+ $fourth-stop: grayscale($fourth-stop);
+ $inset-shadow: grayscale($inset-shadow);
+ $second-stop: grayscale($second-stop);
+ $text-shadow: grayscale($text-shadow);
+ $third-stop: grayscale($third-stop);
+ }
+
+ border: 1px solid $border;
+ border-bottom: 1px solid $border-bottom;
+ border-radius: 5px;
+ box-shadow: inset 0 1px 0 0 $inset-shadow;
+ color: $color;
+ display: inline-block;
+ font-size: 14px;
+ font-weight: bold;
+ @include linear-gradient(top, $base-color 0%, $second-stop 50%, $third-stop 50%, $fourth-stop 100%);
+ padding: 8px 20px;
+ text-align: center;
+ text-decoration: none;
+ text-shadow: 0 -1px 1px $text-shadow;
+
+ &:hover:not(:disabled) {
+ $first-stop-hover: adjust-color($base-color, $red: -13, $green: -15, $blue: -18);
+ $second-stop-hover: adjust-color($base-color, $red: -66, $green: -62, $blue: -51);
+ $third-stop-hover: adjust-color($base-color, $red: -93, $green: -85, $blue: -66);
+ $fourth-stop-hover: adjust-color($base-color, $red: -86, $green: -80, $blue: -63);
+
+ @if $grayscale == true {
+ $first-stop-hover: grayscale($first-stop-hover);
+ $second-stop-hover: grayscale($second-stop-hover);
+ $third-stop-hover: grayscale($third-stop-hover);
+ $fourth-stop-hover: grayscale($fourth-stop-hover);
+ }
+
+ cursor: pointer;
+ @include linear-gradient(top, $first-stop-hover 0%,
+ $second-stop-hover 50%,
+ $third-stop-hover 50%,
+ $fourth-stop-hover 100%);
+ }
+
+ &:active:not(:disabled) {
+ $inset-shadow-active: adjust-color($base-color, $red: -111, $green: -116, $blue: -122);
+
+ @if $grayscale == true {
+ $inset-shadow-active: grayscale($inset-shadow-active);
+ }
+
+ box-shadow: inset 0 0 20px 0 $inset-shadow-active, 0 1px 0 #fff;
+ }
+}
+
+
+// Pill Button
+//************************************************************************//
+@mixin pill($base-color, $grayscale: false) {
+ $color: hsl(0, 0, 100%);
+ $border-bottom: adjust-color($base-color, $hue: 8, $saturation: -11%, $lightness: -26%);
+ $border-sides: adjust-color($base-color, $hue: 4, $saturation: -21%, $lightness: -21%);
+ $border-top: adjust-color($base-color, $hue: -1, $saturation: -30%, $lightness: -15%);
+ $inset-shadow: adjust-color($base-color, $hue: -1, $saturation: -1%, $lightness: 7%);
+ $stop-gradient: adjust-color($base-color, $hue: 8, $saturation: 14%, $lightness: -10%);
+ $text-shadow: adjust-color($base-color, $hue: 5, $saturation: -19%, $lightness: -15%);
+
+ @if lightness($base-color) > 70% {
+ $color: hsl(0, 0, 20%);
+ $text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%);
+ }
+
+ @if $grayscale == true {
+ $border-bottom: grayscale($border-bottom);
+ $border-sides: grayscale($border-sides);
+ $border-top: grayscale($border-top);
+ $inset-shadow: grayscale($inset-shadow);
+ $stop-gradient: grayscale($stop-gradient);
+ $text-shadow: grayscale($text-shadow);
+ }
+
+ border: 1px solid $border-top;
+ border-color: $border-top $border-sides $border-bottom;
+ border-radius: 16px;
+ box-shadow: inset 0 1px 0 0 $inset-shadow, 0 1px 2px 0 #b3b3b3;
+ color: $color;
+ display: inline-block;
+ font-size: 11px;
+ font-weight: normal;
+ line-height: 1;
+ @include linear-gradient ($base-color, $stop-gradient);
+ padding: 5px 16px;
+ text-align: center;
+ text-decoration: none;
+ text-shadow: 0 -1px 1px $text-shadow;
+ background-clip: padding-box;
+
+ &:hover:not(:disabled) {
+ $base-color-hover: adjust-color($base-color, $lightness: -4.5%);
+ $border-bottom: adjust-color($base-color, $hue: 8, $saturation: 13.5%, $lightness: -32%);
+ $border-sides: adjust-color($base-color, $hue: 4, $saturation: -2%, $lightness: -27%);
+ $border-top: adjust-color($base-color, $hue: -1, $saturation: -17%, $lightness: -21%);
+ $inset-shadow-hover: adjust-color($base-color, $saturation: -1%, $lightness: 3%);
+ $stop-gradient-hover: adjust-color($base-color, $hue: 8, $saturation: -4%, $lightness: -15.5%);
+ $text-shadow-hover: adjust-color($base-color, $hue: 5, $saturation: -5%, $lightness: -22%);
+
+ @if $grayscale == true {
+ $base-color-hover: grayscale($base-color-hover);
+ $border-bottom: grayscale($border-bottom);
+ $border-sides: grayscale($border-sides);
+ $border-top: grayscale($border-top);
+ $inset-shadow-hover: grayscale($inset-shadow-hover);
+ $stop-gradient-hover: grayscale($stop-gradient-hover);
+ $text-shadow-hover: grayscale($text-shadow-hover);
+ }
+
+ border: 1px solid $border-top;
+ border-color: $border-top $border-sides $border-bottom;
+ box-shadow: inset 0 1px 0 0 $inset-shadow-hover;
+ cursor: pointer;
+ @include linear-gradient ($base-color-hover, $stop-gradient-hover);
+ text-shadow: 0 -1px 1px $text-shadow-hover;
+ background-clip: padding-box;
+ }
+
+ &:active:not(:disabled) {
+ $active-color: adjust-color($base-color, $hue: 4, $saturation: -12%, $lightness: -10%);
+ $border-active: adjust-color($base-color, $hue: 6, $saturation: -2.5%, $lightness: -30%);
+ $border-bottom-active: adjust-color($base-color, $hue: 11, $saturation: 6%, $lightness: -31%);
+ $inset-shadow-active: adjust-color($base-color, $hue: 9, $saturation: 2%, $lightness: -21.5%);
+ $text-shadow-active: adjust-color($base-color, $hue: 5, $saturation: -12%, $lightness: -21.5%);
+
+ @if $grayscale == true {
+ $active-color: grayscale($active-color);
+ $border-active: grayscale($border-active);
+ $border-bottom-active: grayscale($border-bottom-active);
+ $inset-shadow-active: grayscale($inset-shadow-active);
+ $text-shadow-active: grayscale($text-shadow-active);
+ }
+
+ background: $active-color;
+ border: 1px solid $border-active;
+ border-bottom: 1px solid $border-bottom-active;
+ box-shadow: inset 0 0 6px 3px $inset-shadow-active, 0 1px 0 0 #fff;
+ text-shadow: 0 -1px 1px $text-shadow-active;
+ }
+}
diff --git a/docs/_static/css/bourbon/addons/_clearfix.scss b/docs/_static/css/bourbon/addons/_clearfix.scss
new file mode 100644
index 0000000..ca9903c
--- /dev/null
+++ b/docs/_static/css/bourbon/addons/_clearfix.scss
@@ -0,0 +1,29 @@
+// Micro clearfix provides an easy way to contain floats without adding additional markup
+//
+// Example usage:
+//
+// // Contain all floats within .wrapper
+// .wrapper {
+// @include clearfix;
+// .content,
+// .sidebar {
+// float : left;
+// }
+// }
+
+@mixin clearfix {
+ *zoom: 1;
+
+ &:before,
+ &:after {
+ content: " ";
+ display: table;
+ }
+
+ &:after {
+ clear: both;
+ }
+}
+
+// Acknowledgements
+// Micro clearfix: [Nicolas Gallagher](http://nicolasgallagher.com/micro-clearfix-hack/)
diff --git a/docs/_static/css/bourbon/addons/_font-family.scss b/docs/_static/css/bourbon/addons/_font-family.scss
new file mode 100644
index 0000000..df8a80d
--- /dev/null
+++ b/docs/_static/css/bourbon/addons/_font-family.scss
@@ -0,0 +1,5 @@
+$georgia: Georgia, Cambria, "Times New Roman", Times, serif;
+$helvetica: "Helvetica Neue", Helvetica, Arial, sans-serif;
+$lucida-grande: "Lucida Grande", Tahoma, Verdana, Arial, sans-serif;
+$monospace: "Bitstream Vera Sans Mono", Consolas, Courier, monospace;
+$verdana: Verdana, Geneva, sans-serif;
diff --git a/docs/_static/css/bourbon/addons/_hide-text.scss b/docs/_static/css/bourbon/addons/_hide-text.scss
new file mode 100644
index 0000000..68d4bf8
--- /dev/null
+++ b/docs/_static/css/bourbon/addons/_hide-text.scss
@@ -0,0 +1,5 @@
+@mixin hide-text {
+ color: transparent;
+ font: 0/0 a;
+ text-shadow: none;
+}
diff --git a/docs/_static/css/bourbon/addons/_html5-input-types.scss b/docs/_static/css/bourbon/addons/_html5-input-types.scss
new file mode 100644
index 0000000..b184382
--- /dev/null
+++ b/docs/_static/css/bourbon/addons/_html5-input-types.scss
@@ -0,0 +1,56 @@
+//************************************************************************//
+// Generate a variable ($all-text-inputs) with a list of all html5
+// input types that have a text-based input, excluding textarea.
+// http://diveintohtml5.org/forms.html
+//************************************************************************//
+$inputs-list: 'input[type="email"]',
+ 'input[type="number"]',
+ 'input[type="password"]',
+ 'input[type="search"]',
+ 'input[type="tel"]',
+ 'input[type="text"]',
+ 'input[type="url"]',
+
+ // Webkit & Gecko may change the display of these in the future
+ 'input[type="color"]',
+ 'input[type="date"]',
+ 'input[type="datetime"]',
+ 'input[type="datetime-local"]',
+ 'input[type="month"]',
+ 'input[type="time"]',
+ 'input[type="week"]';
+
+$unquoted-inputs-list: ();
+@each $input-type in $inputs-list {
+ $unquoted-inputs-list: append($unquoted-inputs-list, unquote($input-type), comma);
+}
+
+$all-text-inputs: $unquoted-inputs-list;
+
+
+// Hover Pseudo-class
+//************************************************************************//
+$all-text-inputs-hover: ();
+@each $input-type in $unquoted-inputs-list {
+ $input-type-hover: $input-type + ":hover";
+ $all-text-inputs-hover: append($all-text-inputs-hover, $input-type-hover, comma);
+}
+
+// Focus Pseudo-class
+//************************************************************************//
+$all-text-inputs-focus: ();
+@each $input-type in $unquoted-inputs-list {
+ $input-type-focus: $input-type + ":focus";
+ $all-text-inputs-focus: append($all-text-inputs-focus, $input-type-focus, comma);
+}
+
+// You must use interpolation on the variable:
+// #{$all-text-inputs}
+// #{$all-text-inputs-hover}
+// #{$all-text-inputs-focus}
+
+// Example
+//************************************************************************//
+// #{$all-text-inputs}, textarea {
+// border: 1px solid red;
+// }
diff --git a/docs/_static/css/bourbon/addons/_position.scss b/docs/_static/css/bourbon/addons/_position.scss
new file mode 100644
index 0000000..faad1ca
--- /dev/null
+++ b/docs/_static/css/bourbon/addons/_position.scss
@@ -0,0 +1,42 @@
+@mixin position ($position: relative, $coordinates: 0 0 0 0) {
+
+ @if type-of($position) == list {
+ $coordinates: $position;
+ $position: relative;
+ }
+
+ $top: nth($coordinates, 1);
+ $right: nth($coordinates, 2);
+ $bottom: nth($coordinates, 3);
+ $left: nth($coordinates, 4);
+
+ position: $position;
+
+ @if $top == auto {
+ top: $top;
+ }
+ @else if not(unitless($top)) {
+ top: $top;
+ }
+
+ @if $right == auto {
+ right: $right;
+ }
+ @else if not(unitless($right)) {
+ right: $right;
+ }
+
+ @if $bottom == auto {
+ bottom: $bottom;
+ }
+ @else if not(unitless($bottom)) {
+ bottom: $bottom;
+ }
+
+ @if $left == auto {
+ left: $left;
+ }
+ @else if not(unitless($left)) {
+ left: $left;
+ }
+}
diff --git a/docs/_static/css/bourbon/addons/_prefixer.scss b/docs/_static/css/bourbon/addons/_prefixer.scss
new file mode 100644
index 0000000..6bfd23a
--- /dev/null
+++ b/docs/_static/css/bourbon/addons/_prefixer.scss
@@ -0,0 +1,49 @@
+//************************************************************************//
+// Example: @include prefixer(border-radius, $radii, webkit ms spec);
+//************************************************************************//
+$prefix-for-webkit: true !default;
+$prefix-for-mozilla: true !default;
+$prefix-for-microsoft: true !default;
+$prefix-for-opera: true !default;
+$prefix-for-spec: true !default; // required for keyframe mixin
+
+@mixin prefixer ($property, $value, $prefixes) {
+ @each $prefix in $prefixes {
+ @if $prefix == webkit {
+ @if $prefix-for-webkit {
+ -webkit-#{$property}: $value;
+ }
+ }
+ @else if $prefix == moz {
+ @if $prefix-for-mozilla {
+ -moz-#{$property}: $value;
+ }
+ }
+ @else if $prefix == ms {
+ @if $prefix-for-microsoft {
+ -ms-#{$property}: $value;
+ }
+ }
+ @else if $prefix == o {
+ @if $prefix-for-opera {
+ -o-#{$property}: $value;
+ }
+ }
+ @else if $prefix == spec {
+ @if $prefix-for-spec {
+ #{$property}: $value;
+ }
+ }
+ @else {
+ @warn "Unrecognized prefix: #{$prefix}";
+ }
+ }
+}
+
+@mixin disable-prefix-for-all() {
+ $prefix-for-webkit: false;
+ $prefix-for-mozilla: false;
+ $prefix-for-microsoft: false;
+ $prefix-for-opera: false;
+ $prefix-for-spec: false;
+}
diff --git a/docs/_static/css/bourbon/addons/_retina-image.scss b/docs/_static/css/bourbon/addons/_retina-image.scss
new file mode 100644
index 0000000..a84b6fa
--- /dev/null
+++ b/docs/_static/css/bourbon/addons/_retina-image.scss
@@ -0,0 +1,32 @@
+@mixin retina-image($filename, $background-size, $extension: png, $retina-filename: null, $asset-pipeline: false) {
+ @if $asset-pipeline {
+ background-image: image-url("#{$filename}.#{$extension}");
+ }
+ @else {
+ background-image: url("#{$filename}.#{$extension}");
+ }
+
+ @include hidpi {
+
+ @if $asset-pipeline {
+ @if $retina-filename {
+ background-image: image-url("#{$retina-filename}.#{$extension}");
+ }
+ @else {
+ background-image: image-url("#{$filename}@2x.#{$extension}");
+ }
+ }
+
+ @else {
+ @if $retina-filename {
+ background-image: url("#{$retina-filename}.#{$extension}");
+ }
+ @else {
+ background-image: url("#{$filename}@2x.#{$extension}");
+ }
+ }
+
+ background-size: $background-size;
+
+ }
+}
diff --git a/docs/_static/css/bourbon/addons/_size.scss b/docs/_static/css/bourbon/addons/_size.scss
new file mode 100644
index 0000000..342e41b
--- /dev/null
+++ b/docs/_static/css/bourbon/addons/_size.scss
@@ -0,0 +1,44 @@
+@mixin size($size) {
+ @if length($size) == 1 {
+ @if $size == auto {
+ width: $size;
+ height: $size;
+ }
+
+ @else if unitless($size) {
+ width: $size + px;
+ height: $size + px;
+ }
+
+ @else if not(unitless($size)) {
+ width: $size;
+ height: $size;
+ }
+ }
+
+ // Width x Height
+ @if length($size) == 2 {
+ $width: nth($size, 1);
+ $height: nth($size, 2);
+
+ @if $width == auto {
+ width: $width;
+ }
+ @else if not(unitless($width)) {
+ width: $width;
+ }
+ @else if unitless($width) {
+ width: $width + px;
+ }
+
+ @if $height == auto {
+ height: $height;
+ }
+ @else if not(unitless($height)) {
+ height: $height;
+ }
+ @else if unitless($height) {
+ height: $height + px;
+ }
+ }
+}
diff --git a/docs/_static/css/bourbon/addons/_timing-functions.scss b/docs/_static/css/bourbon/addons/_timing-functions.scss
new file mode 100644
index 0000000..51b2410
--- /dev/null
+++ b/docs/_static/css/bourbon/addons/_timing-functions.scss
@@ -0,0 +1,32 @@
+// CSS cubic-bezier timing functions. Timing functions courtesy of jquery.easie (github.com/jaukia/easie)
+// Timing functions are the same as demo'ed here: http://jqueryui.com/demos/effect/easing.html
+
+// EASE IN
+$ease-in-quad: cubic-bezier(0.550, 0.085, 0.680, 0.530);
+$ease-in-cubic: cubic-bezier(0.550, 0.055, 0.675, 0.190);
+$ease-in-quart: cubic-bezier(0.895, 0.030, 0.685, 0.220);
+$ease-in-quint: cubic-bezier(0.755, 0.050, 0.855, 0.060);
+$ease-in-sine: cubic-bezier(0.470, 0.000, 0.745, 0.715);
+$ease-in-expo: cubic-bezier(0.950, 0.050, 0.795, 0.035);
+$ease-in-circ: cubic-bezier(0.600, 0.040, 0.980, 0.335);
+$ease-in-back: cubic-bezier(0.600, -0.280, 0.735, 0.045);
+
+// EASE OUT
+$ease-out-quad: cubic-bezier(0.250, 0.460, 0.450, 0.940);
+$ease-out-cubic: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+$ease-out-quart: cubic-bezier(0.165, 0.840, 0.440, 1.000);
+$ease-out-quint: cubic-bezier(0.230, 1.000, 0.320, 1.000);
+$ease-out-sine: cubic-bezier(0.390, 0.575, 0.565, 1.000);
+$ease-out-expo: cubic-bezier(0.190, 1.000, 0.220, 1.000);
+$ease-out-circ: cubic-bezier(0.075, 0.820, 0.165, 1.000);
+$ease-out-back: cubic-bezier(0.175, 0.885, 0.320, 1.275);
+
+// EASE IN OUT
+$ease-in-out-quad: cubic-bezier(0.455, 0.030, 0.515, 0.955);
+$ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1.000);
+$ease-in-out-quart: cubic-bezier(0.770, 0.000, 0.175, 1.000);
+$ease-in-out-quint: cubic-bezier(0.860, 0.000, 0.070, 1.000);
+$ease-in-out-sine: cubic-bezier(0.445, 0.050, 0.550, 0.950);
+$ease-in-out-expo: cubic-bezier(1.000, 0.000, 0.000, 1.000);
+$ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.150, 0.860);
+$ease-in-out-back: cubic-bezier(0.680, -0.550, 0.265, 1.550);
diff --git a/docs/_static/css/bourbon/addons/_triangle.scss b/docs/_static/css/bourbon/addons/_triangle.scss
new file mode 100644
index 0000000..0e02aca
--- /dev/null
+++ b/docs/_static/css/bourbon/addons/_triangle.scss
@@ -0,0 +1,45 @@
+@mixin triangle ($size, $color, $direction) {
+ height: 0;
+ width: 0;
+
+ @if ($direction == up) or ($direction == down) or ($direction == right) or ($direction == left) {
+ border-color: transparent;
+ border-style: solid;
+ border-width: $size / 2;
+
+ @if $direction == up {
+ border-bottom-color: $color;
+
+ } @else if $direction == right {
+ border-left-color: $color;
+
+ } @else if $direction == down {
+ border-top-color: $color;
+
+ } @else if $direction == left {
+ border-right-color: $color;
+ }
+ }
+
+ @else if ($direction == up-right) or ($direction == up-left) {
+ border-top: $size solid $color;
+
+ @if $direction == up-right {
+ border-left: $size solid transparent;
+
+ } @else if $direction == up-left {
+ border-right: $size solid transparent;
+ }
+ }
+
+ @else if ($direction == down-right) or ($direction == down-left) {
+ border-bottom: $size solid $color;
+
+ @if $direction == down-right {
+ border-left: $size solid transparent;
+
+ } @else if $direction == down-left {
+ border-right: $size solid transparent;
+ }
+ }
+}
diff --git a/docs/_static/css/bourbon/css3/_animation.scss b/docs/_static/css/bourbon/css3/_animation.scss
new file mode 100644
index 0000000..08c3dbf
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_animation.scss
@@ -0,0 +1,52 @@
+// http://www.w3.org/TR/css3-animations/#the-animation-name-property-
+// Each of these mixins support comma separated lists of values, which allows different transitions for individual properties to be described in a single style rule. Each value in the list corresponds to the value at that same position in the other properties.
+
+// Official animation shorthand property.
+@mixin animation ($animations...) {
+ @include prefixer(animation, $animations, webkit moz spec);
+}
+
+// Individual Animation Properties
+@mixin animation-name ($names...) {
+ @include prefixer(animation-name, $names, webkit moz spec);
+}
+
+
+@mixin animation-duration ($times...) {
+ @include prefixer(animation-duration, $times, webkit moz spec);
+}
+
+
+@mixin animation-timing-function ($motions...) {
+// ease | linear | ease-in | ease-out | ease-in-out
+ @include prefixer(animation-timing-function, $motions, webkit moz spec);
+}
+
+
+@mixin animation-iteration-count ($values...) {
+// infinite |
+ @include prefixer(animation-iteration-count, $values, webkit moz spec);
+}
+
+
+@mixin animation-direction ($directions...) {
+// normal | alternate
+ @include prefixer(animation-direction, $directions, webkit moz spec);
+}
+
+
+@mixin animation-play-state ($states...) {
+// running | paused
+ @include prefixer(animation-play-state, $states, webkit moz spec);
+}
+
+
+@mixin animation-delay ($times...) {
+ @include prefixer(animation-delay, $times, webkit moz spec);
+}
+
+
+@mixin animation-fill-mode ($modes...) {
+// none | forwards | backwards | both
+ @include prefixer(animation-fill-mode, $modes, webkit moz spec);
+}
diff --git a/docs/_static/css/bourbon/css3/_appearance.scss b/docs/_static/css/bourbon/css3/_appearance.scss
new file mode 100644
index 0000000..3eb16e4
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_appearance.scss
@@ -0,0 +1,3 @@
+@mixin appearance ($value) {
+ @include prefixer(appearance, $value, webkit moz ms o spec);
+}
diff --git a/docs/_static/css/bourbon/css3/_backface-visibility.scss b/docs/_static/css/bourbon/css3/_backface-visibility.scss
new file mode 100644
index 0000000..1161fe6
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_backface-visibility.scss
@@ -0,0 +1,6 @@
+//************************************************************************//
+// Backface-visibility mixin
+//************************************************************************//
+@mixin backface-visibility($visibility) {
+ @include prefixer(backface-visibility, $visibility, webkit spec);
+}
diff --git a/docs/_static/css/bourbon/css3/_background-image.scss b/docs/_static/css/bourbon/css3/_background-image.scss
new file mode 100644
index 0000000..17016b9
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_background-image.scss
@@ -0,0 +1,48 @@
+//************************************************************************//
+// Background-image property for adding multiple background images with
+// gradients, or for stringing multiple gradients together.
+//************************************************************************//
+
+@mixin background-image($images...) {
+ background-image: _add-prefix($images, webkit);
+ background-image: _add-prefix($images);
+}
+
+@function _add-prefix($images, $vendor: false) {
+ $images-prefixed: ();
+ $gradient-positions: false;
+ @for $i from 1 through length($images) {
+ $type: type-of(nth($images, $i)); // Get type of variable - List or String
+
+ // If variable is a list - Gradient
+ @if $type == list {
+ $gradient-type: nth(nth($images, $i), 1); // linear or radial
+ $gradient-pos: null;
+ $gradient-args: null;
+
+ @if ($gradient-type == linear) or ($gradient-type == radial) {
+ $gradient-pos: nth(nth($images, $i), 2); // Get gradient position
+ $gradient-args: nth(nth($images, $i), 3); // Get actual gradient (red, blue)
+ }
+ @else {
+ $gradient-args: nth(nth($images, $i), 2); // Get actual gradient (red, blue)
+ }
+
+ $gradient-positions: _gradient-positions-parser($gradient-type, $gradient-pos);
+ $gradient: _render-gradients($gradient-positions, $gradient-args, $gradient-type, $vendor);
+ $images-prefixed: append($images-prefixed, $gradient, comma);
+ }
+ // If variable is a string - Image
+ @else if $type == string {
+ $images-prefixed: join($images-prefixed, nth($images, $i), comma);
+ }
+ }
+ @return $images-prefixed;
+}
+
+//Examples:
+ //@include background-image(linear-gradient(top, orange, red));
+ //@include background-image(radial-gradient(50% 50%, cover circle, orange, red));
+ //@include background-image(url("/images/a.png"), linear-gradient(orange, red));
+ //@include background-image(url("image.png"), linear-gradient(orange, red), url("image.png"));
+ //@include background-image(linear-gradient(hsla(0, 100%, 100%, 0.25) 0%, hsla(0, 100%, 100%, 0.08) 50%, transparent 50%), linear-gradient(orange, red));
diff --git a/docs/_static/css/bourbon/css3/_background.scss b/docs/_static/css/bourbon/css3/_background.scss
new file mode 100644
index 0000000..766d5d3
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_background.scss
@@ -0,0 +1,103 @@
+//************************************************************************//
+// Background property for adding multiple backgrounds using shorthand
+// notation.
+//************************************************************************//
+
+@mixin background(
+ $background-1 , $background-2: false,
+ $background-3: false, $background-4: false,
+ $background-5: false, $background-6: false,
+ $background-7: false, $background-8: false,
+ $background-9: false, $background-10: false,
+ $fallback: false
+) {
+ $backgrounds: compact($background-1, $background-2,
+ $background-3, $background-4,
+ $background-5, $background-6,
+ $background-7, $background-8,
+ $background-9, $background-10);
+
+ $fallback-color: false;
+ @if (type-of($fallback) == color) or ($fallback == "transparent") {
+ $fallback-color: $fallback;
+ }
+ @else {
+ $fallback-color: _extract-background-color($backgrounds);
+ }
+
+ @if $fallback-color {
+ background-color: $fallback-color;
+ }
+ background: _background-add-prefix($backgrounds, webkit);
+ background: _background-add-prefix($backgrounds);
+}
+
+@function _extract-background-color($backgrounds) {
+ $final-bg-layer: nth($backgrounds, length($backgrounds));
+ @if type-of($final-bg-layer) == list {
+ @for $i from 1 through length($final-bg-layer) {
+ $value: nth($final-bg-layer, $i);
+ @if type-of($value) == color {
+ @return $value;
+ }
+ }
+ }
+ @return false;
+}
+
+@function _background-add-prefix($backgrounds, $vendor: false) {
+ $backgrounds-prefixed: ();
+
+ @for $i from 1 through length($backgrounds) {
+ $shorthand: nth($backgrounds, $i); // Get member for current index
+ $type: type-of($shorthand); // Get type of variable - List (gradient) or String (image)
+
+ // If shorthand is a list (gradient)
+ @if $type == list {
+ $first-member: nth($shorthand, 1); // Get first member of shorthand
+
+ // Linear Gradient
+ @if index(linear radial, nth($first-member, 1)) {
+ $gradient-type: nth($first-member, 1); // linear || radial
+ $gradient-args: false;
+ $gradient-positions: false;
+ $shorthand-start: false;
+ @if type-of($first-member) == list { // Linear gradient plus additional shorthand values - lg(red,orange)repeat,...
+ $gradient-positions: nth($first-member, 2);
+ $gradient-args: nth($first-member, 3);
+ $shorthand-start: 2;
+ }
+ @else { // Linear gradient only - lg(red,orange),...
+ $gradient-positions: nth($shorthand, 2);
+ $gradient-args: nth($shorthand, 3); // Get gradient (red, blue)
+ }
+
+ $gradient-positions: _gradient-positions-parser($gradient-type, $gradient-positions);
+ $gradient: _render-gradients($gradient-positions, $gradient-args, $gradient-type, $vendor);
+
+ // Append any additional shorthand args to gradient
+ @if $shorthand-start {
+ @for $j from $shorthand-start through length($shorthand) {
+ $gradient: join($gradient, nth($shorthand, $j), space);
+ }
+ }
+ $backgrounds-prefixed: append($backgrounds-prefixed, $gradient, comma);
+ }
+ // Image with additional properties
+ @else {
+ $backgrounds-prefixed: append($backgrounds-prefixed, $shorthand, comma);
+ }
+ }
+ // If shorthand is a simple string (color or image)
+ @else if $type == string {
+ $backgrounds-prefixed: join($backgrounds-prefixed, $shorthand, comma);
+ }
+ }
+ @return $backgrounds-prefixed;
+}
+
+//Examples:
+ //@include background(linear-gradient(top, orange, red));
+ //@include background(radial-gradient(circle at 40% 40%, orange, red));
+ //@include background(url("/images/a.png") no-repeat, linear-gradient(orange, red));
+ //@include background(url("image.png") center center, linear-gradient(orange, red), url("image.png"));
diff --git a/docs/_static/css/bourbon/css3/_border-image.scss b/docs/_static/css/bourbon/css3/_border-image.scss
new file mode 100644
index 0000000..1fff212
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_border-image.scss
@@ -0,0 +1,55 @@
+@mixin border-image($images) {
+ -webkit-border-image: _border-add-prefix($images, webkit);
+ -moz-border-image: _border-add-prefix($images, moz);
+ -o-border-image: _border-add-prefix($images, o);
+ border-image: _border-add-prefix($images);
+}
+
+@function _border-add-prefix($images, $vendor: false) {
+ $border-image: null;
+ $images-type: type-of(nth($images, 1));
+ $first-var: nth(nth($images, 1), 1); // Get type of Gradient (Linear || radial)
+
+ // If input is a gradient
+ @if $images-type == string {
+ @if ($first-var == "linear") or ($first-var == "radial") {
+ $gradient-type: nth($images, 1); // Get type of gradient (linear || radial)
+ $gradient-pos: nth($images, 2); // Get gradient position
+ $gradient-args: nth($images, 3); // Get actual gradient (red, blue)
+ $gradient-positions: _gradient-positions-parser($gradient-type, $gradient-pos);
+ $border-image: _render-gradients($gradient-positions, $gradient-args, $gradient-type, $vendor);
+ }
+ // If input is a URL
+ @else {
+ $border-image: $images;
+ }
+ }
+ // If input is gradient or url + additional args
+ @else if $images-type == list {
+ $type: type-of(nth($images, 1)); // Get type of variable - List or String
+
+ // If variable is a list - Gradient
+ @if $type == list {
+ $gradient: nth($images, 1);
+ $gradient-type: nth($gradient, 1); // Get type of gradient (linear || radial)
+ $gradient-pos: nth($gradient, 2); // Get gradient position
+ $gradient-args: nth($gradient, 3); // Get actual gradient (red, blue)
+ $gradient-positions: _gradient-positions-parser($gradient-type, $gradient-pos);
+ $border-image: _render-gradients($gradient-positions, $gradient-args, $gradient-type, $vendor);
+
+ @for $i from 2 through length($images) {
+ $border-image: append($border-image, nth($images, $i));
+ }
+ }
+ }
+ @return $border-image;
+}
+
+//Examples:
+// @include border-image(url("image.png"));
+// @include border-image(url("image.png") 20 stretch);
+// @include border-image(linear-gradient(45deg, orange, yellow));
+// @include border-image(linear-gradient(45deg, orange, yellow) stretch);
+// @include border-image(linear-gradient(45deg, orange, yellow) 20 30 40 50 stretch round);
+// @include border-image(radial-gradient(top, cover, orange, yellow, orange));
+
diff --git a/docs/_static/css/bourbon/css3/_border-radius.scss b/docs/_static/css/bourbon/css3/_border-radius.scss
new file mode 100644
index 0000000..7c17190
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_border-radius.scss
@@ -0,0 +1,22 @@
+//************************************************************************//
+// Shorthand Border-radius mixins
+//************************************************************************//
+@mixin border-top-radius($radii) {
+ @include prefixer(border-top-left-radius, $radii, spec);
+ @include prefixer(border-top-right-radius, $radii, spec);
+}
+
+@mixin border-bottom-radius($radii) {
+ @include prefixer(border-bottom-left-radius, $radii, spec);
+ @include prefixer(border-bottom-right-radius, $radii, spec);
+}
+
+@mixin border-left-radius($radii) {
+ @include prefixer(border-top-left-radius, $radii, spec);
+ @include prefixer(border-bottom-left-radius, $radii, spec);
+}
+
+@mixin border-right-radius($radii) {
+ @include prefixer(border-top-right-radius, $radii, spec);
+ @include prefixer(border-bottom-right-radius, $radii, spec);
+}
diff --git a/docs/_static/css/bourbon/css3/_box-sizing.scss b/docs/_static/css/bourbon/css3/_box-sizing.scss
new file mode 100644
index 0000000..f07e1d4
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_box-sizing.scss
@@ -0,0 +1,4 @@
+@mixin box-sizing ($box) {
+// content-box | border-box | inherit
+ @include prefixer(box-sizing, $box, webkit moz spec);
+}
diff --git a/docs/_static/css/bourbon/css3/_columns.scss b/docs/_static/css/bourbon/css3/_columns.scss
new file mode 100644
index 0000000..42274a4
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_columns.scss
@@ -0,0 +1,47 @@
+@mixin columns($arg: auto) {
+// ||
+ @include prefixer(columns, $arg, webkit moz spec);
+}
+
+@mixin column-count($int: auto) {
+// auto || integer
+ @include prefixer(column-count, $int, webkit moz spec);
+}
+
+@mixin column-gap($length: normal) {
+// normal || length
+ @include prefixer(column-gap, $length, webkit moz spec);
+}
+
+@mixin column-fill($arg: auto) {
+// auto || length
+ @include prefixer(columns-fill, $arg, webkit moz spec);
+}
+
+@mixin column-rule($arg) {
+// || ||
+ @include prefixer(column-rule, $arg, webkit moz spec);
+}
+
+@mixin column-rule-color($color) {
+ @include prefixer(column-rule-color, $color, webkit moz spec);
+}
+
+@mixin column-rule-style($style: none) {
+// none | hidden | dashed | dotted | double | groove | inset | inset | outset | ridge | solid
+ @include prefixer(column-rule-style, $style, webkit moz spec);
+}
+
+@mixin column-rule-width ($width: none) {
+ @include prefixer(column-rule-width, $width, webkit moz spec);
+}
+
+@mixin column-span($arg: none) {
+// none || all
+ @include prefixer(column-span, $arg, webkit moz spec);
+}
+
+@mixin column-width($length: auto) {
+// auto || length
+ @include prefixer(column-width, $length, webkit moz spec);
+}
diff --git a/docs/_static/css/bourbon/css3/_flex-box.scss b/docs/_static/css/bourbon/css3/_flex-box.scss
new file mode 100644
index 0000000..3e741e6
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_flex-box.scss
@@ -0,0 +1,52 @@
+// CSS3 Flexible Box Model and property defaults
+
+// Custom shorthand notation for flexbox
+@mixin box($orient: inline-axis, $pack: start, $align: stretch) {
+ @include display-box;
+ @include box-orient($orient);
+ @include box-pack($pack);
+ @include box-align($align);
+}
+
+@mixin display-box {
+ display: -webkit-box;
+ display: -moz-box;
+ display: box;
+}
+
+@mixin box-orient($orient: inline-axis) {
+// horizontal|vertical|inline-axis|block-axis|inherit
+ @include prefixer(box-orient, $orient, webkit moz spec);
+}
+
+@mixin box-pack($pack: start) {
+// start|end|center|justify
+ @include prefixer(box-pack, $pack, webkit moz spec);
+}
+
+@mixin box-align($align: stretch) {
+// start|end|center|baseline|stretch
+ @include prefixer(box-align, $align, webkit moz spec);
+}
+
+@mixin box-direction($direction: normal) {
+// normal|reverse|inherit
+ @include prefixer(box-direction, $direction, webkit moz spec);
+}
+
+@mixin box-lines($lines: single) {
+// single|multiple
+ @include prefixer(box-lines, $lines, webkit moz spec);
+}
+
+@mixin box-ordinal-group($int: 1) {
+ @include prefixer(box-ordinal-group, $int, webkit moz spec);
+}
+
+@mixin box-flex($value: 0.0) {
+ @include prefixer(box-flex, $value, webkit moz spec);
+}
+
+@mixin box-flex-group($int: 1) {
+ @include prefixer(box-flex-group, $int, webkit moz spec);
+}
diff --git a/docs/_static/css/bourbon/css3/_font-face.scss b/docs/_static/css/bourbon/css3/_font-face.scss
new file mode 100644
index 0000000..029ee8f
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_font-face.scss
@@ -0,0 +1,23 @@
+// Order of the includes matters, and it is: normal, bold, italic, bold+italic.
+
+@mixin font-face($font-family, $file-path, $weight: normal, $style: normal, $asset-pipeline: false ) {
+ @font-face {
+ font-family: $font-family;
+ font-weight: $weight;
+ font-style: $style;
+
+ @if $asset-pipeline == true {
+ src: font-url('#{$file-path}.eot');
+ src: font-url('#{$file-path}.eot?#iefix') format('embedded-opentype'),
+ font-url('#{$file-path}.woff') format('woff'),
+ font-url('#{$file-path}.ttf') format('truetype'),
+ font-url('#{$file-path}.svg##{$font-family}') format('svg');
+ } @else {
+ src: url('#{$file-path}.eot');
+ src: url('#{$file-path}.eot?#iefix') format('embedded-opentype'),
+ url('#{$file-path}.woff') format('woff'),
+ url('#{$file-path}.ttf') format('truetype'),
+ url('#{$file-path}.svg##{$font-family}') format('svg');
+ }
+ }
+}
diff --git a/docs/_static/css/bourbon/css3/_hidpi-media-query.scss b/docs/_static/css/bourbon/css3/_hidpi-media-query.scss
new file mode 100644
index 0000000..111e400
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_hidpi-media-query.scss
@@ -0,0 +1,10 @@
+// HiDPI mixin. Default value set to 1.3 to target Google Nexus 7 (http://bjango.com/articles/min-device-pixel-ratio/)
+@mixin hidpi($ratio: 1.3) {
+ @media only screen and (-webkit-min-device-pixel-ratio: $ratio),
+ only screen and (min--moz-device-pixel-ratio: $ratio),
+ only screen and (-o-min-device-pixel-ratio: #{$ratio}/1),
+ only screen and (min-resolution: #{round($ratio*96)}dpi),
+ only screen and (min-resolution: #{$ratio}dppx) {
+ @content;
+ }
+}
diff --git a/docs/_static/css/bourbon/css3/_image-rendering.scss b/docs/_static/css/bourbon/css3/_image-rendering.scss
new file mode 100644
index 0000000..abc7ee1
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_image-rendering.scss
@@ -0,0 +1,13 @@
+@mixin image-rendering ($mode:optimizeQuality) {
+
+ @if ($mode == optimize-contrast) {
+ image-rendering: -moz-crisp-edges;
+ image-rendering: -o-crisp-edges;
+ image-rendering: -webkit-optimize-contrast;
+ image-rendering: optimize-contrast;
+ }
+
+ @else {
+ image-rendering: $mode;
+ }
+}
diff --git a/docs/_static/css/bourbon/css3/_inline-block.scss b/docs/_static/css/bourbon/css3/_inline-block.scss
new file mode 100644
index 0000000..3272a00
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_inline-block.scss
@@ -0,0 +1,8 @@
+// Legacy support for inline-block in IE7 (maybe IE6)
+@mixin inline-block {
+ display: inline-block;
+ vertical-align: baseline;
+ zoom: 1;
+ *display: inline;
+ *vertical-align: auto;
+}
diff --git a/docs/_static/css/bourbon/css3/_keyframes.scss b/docs/_static/css/bourbon/css3/_keyframes.scss
new file mode 100644
index 0000000..dca61f2
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_keyframes.scss
@@ -0,0 +1,43 @@
+// Adds keyframes blocks for supported prefixes, removing redundant prefixes in the block's content
+@mixin keyframes($name) {
+ $original-prefix-for-webkit: $prefix-for-webkit;
+ $original-prefix-for-mozilla: $prefix-for-mozilla;
+ $original-prefix-for-microsoft: $prefix-for-microsoft;
+ $original-prefix-for-opera: $prefix-for-opera;
+ $original-prefix-for-spec: $prefix-for-spec;
+
+ @if $original-prefix-for-webkit {
+ @include disable-prefix-for-all();
+ $prefix-for-webkit: true;
+ @-webkit-keyframes #{$name} {
+ @content;
+ }
+ }
+ @if $original-prefix-for-mozilla {
+ @include disable-prefix-for-all();
+ $prefix-for-mozilla: true;
+ @-moz-keyframes #{$name} {
+ @content;
+ }
+ }
+ @if $original-prefix-for-opera {
+ @include disable-prefix-for-all();
+ $prefix-for-opera: true;
+ @-o-keyframes #{$name} {
+ @content;
+ }
+ }
+ @if $original-prefix-for-spec {
+ @include disable-prefix-for-all();
+ $prefix-for-spec: true;
+ @keyframes #{$name} {
+ @content;
+ }
+ }
+
+ $prefix-for-webkit: $original-prefix-for-webkit;
+ $prefix-for-mozilla: $original-prefix-for-mozilla;
+ $prefix-for-microsoft: $original-prefix-for-microsoft;
+ $prefix-for-opera: $original-prefix-for-opera;
+ $prefix-for-spec: $original-prefix-for-spec;
+}
diff --git a/docs/_static/css/bourbon/css3/_linear-gradient.scss b/docs/_static/css/bourbon/css3/_linear-gradient.scss
new file mode 100644
index 0000000..d5b687b
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_linear-gradient.scss
@@ -0,0 +1,41 @@
+@mixin linear-gradient($pos, $G1, $G2: false,
+ $G3: false, $G4: false,
+ $G5: false, $G6: false,
+ $G7: false, $G8: false,
+ $G9: false, $G10: false,
+ $deprecated-pos1: left top,
+ $deprecated-pos2: left bottom,
+ $fallback: false) {
+ // Detect what type of value exists in $pos
+ $pos-type: type-of(nth($pos, 1));
+ $pos-spec: null;
+ $pos-degree: null;
+
+ // If $pos is missing from mixin, reassign vars and add default position
+ @if ($pos-type == color) or (nth($pos, 1) == "transparent") {
+ $G10: $G9; $G9: $G8; $G8: $G7; $G7: $G6; $G6: $G5;
+ $G5: $G4; $G4: $G3; $G3: $G2; $G2: $G1; $G1: $pos;
+ $pos: null;
+ }
+
+ @if $pos {
+ $positions: _linear-positions-parser($pos);
+ $pos-degree: nth($positions, 1);
+ $pos-spec: nth($positions, 2);
+ }
+
+ $full: compact($G1, $G2, $G3, $G4, $G5, $G6, $G7, $G8, $G9, $G10);
+
+ // Set $G1 as the default fallback color
+ $fallback-color: nth($G1, 1);
+
+ // If $fallback is a color use that color as the fallback color
+ @if (type-of($fallback) == color) or ($fallback == "transparent") {
+ $fallback-color: $fallback;
+ }
+
+ background-color: $fallback-color;
+ background-image: _deprecated-webkit-gradient(linear, $deprecated-pos1, $deprecated-pos2, $full); // Safari <= 5.0
+ background-image: -webkit-linear-gradient($pos-degree $full); // Safari 5.1+, Chrome
+ background-image: unquote("linear-gradient(#{$pos-spec}#{$full})");
+}
diff --git a/docs/_static/css/bourbon/css3/_perspective.scss b/docs/_static/css/bourbon/css3/_perspective.scss
new file mode 100644
index 0000000..0e4deb8
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_perspective.scss
@@ -0,0 +1,8 @@
+@mixin perspective($depth: none) {
+ // none |
+ @include prefixer(perspective, $depth, webkit moz spec);
+}
+
+@mixin perspective-origin($value: 50% 50%) {
+ @include prefixer(perspective-origin, $value, webkit moz spec);
+}
diff --git a/docs/_static/css/bourbon/css3/_placeholder.scss b/docs/_static/css/bourbon/css3/_placeholder.scss
new file mode 100644
index 0000000..22fd92b
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_placeholder.scss
@@ -0,0 +1,29 @@
+$placeholders: '-webkit-input-placeholder',
+ '-moz-placeholder',
+ '-ms-input-placeholder';
+
+@mixin placeholder {
+ @each $placeholder in $placeholders {
+ @if $placeholder == "-webkit-input-placeholder" {
+ &::#{$placeholder} {
+ @content;
+ }
+ }
+ @else if $placeholder == "-moz-placeholder" {
+ // FF 18-
+ &:#{$placeholder} {
+ @content;
+ }
+
+ // FF 19+
+ &::#{$placeholder} {
+ @content;
+ }
+ }
+ @else {
+ &:#{$placeholder} {
+ @content;
+ }
+ }
+ }
+}
diff --git a/docs/_static/css/bourbon/css3/_radial-gradient.scss b/docs/_static/css/bourbon/css3/_radial-gradient.scss
new file mode 100644
index 0000000..e87b45a
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_radial-gradient.scss
@@ -0,0 +1,44 @@
+// Requires Sass 3.1+
+@mixin radial-gradient($G1, $G2,
+ $G3: false, $G4: false,
+ $G5: false, $G6: false,
+ $G7: false, $G8: false,
+ $G9: false, $G10: false,
+ $pos: null,
+ $shape-size: null,
+ $deprecated-pos1: center center,
+ $deprecated-pos2: center center,
+ $deprecated-radius1: 0,
+ $deprecated-radius2: 460,
+ $fallback: false) {
+
+ $data: _radial-arg-parser($G1, $G2, $pos, $shape-size);
+ $G1: nth($data, 1);
+ $G2: nth($data, 2);
+ $pos: nth($data, 3);
+ $shape-size: nth($data, 4);
+
+ $full: compact($G1, $G2, $G3, $G4, $G5, $G6, $G7, $G8, $G9, $G10);
+
+ // Strip deprecated cover/contain for spec
+ $shape-size-spec: _shape-size-stripper($shape-size);
+
+ // Set $G1 as the default fallback color
+ $first-color: nth($full, 1);
+ $fallback-color: nth($first-color, 1);
+
+ @if (type-of($fallback) == color) or ($fallback == "transparent") {
+ $fallback-color: $fallback;
+ }
+
+ // Add Commas and spaces
+ $shape-size: if($shape-size, '#{$shape-size}, ', null);
+ $pos: if($pos, '#{$pos}, ', null);
+ $pos-spec: if($pos, 'at #{$pos}', null);
+ $shape-size-spec: if(($shape-size-spec != ' ') and ($pos == null), '#{$shape-size-spec}, ', '#{$shape-size-spec} ');
+
+ background-color: $fallback-color;
+ background-image: _deprecated-webkit-gradient(radial, $deprecated-pos1, $deprecated-pos2, $full, $deprecated-radius1, $deprecated-radius2); // Safari <= 5.0 && IOS 4
+ background-image: -webkit-radial-gradient(unquote(#{$pos}#{$shape-size}#{$full}));
+ background-image: unquote("radial-gradient(#{$shape-size-spec}#{$pos-spec}#{$full})");
+}
diff --git a/docs/_static/css/bourbon/css3/_transform.scss b/docs/_static/css/bourbon/css3/_transform.scss
new file mode 100644
index 0000000..8cc3596
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_transform.scss
@@ -0,0 +1,15 @@
+@mixin transform($property: none) {
+// none |
+ @include prefixer(transform, $property, webkit moz ms o spec);
+}
+
+@mixin transform-origin($axes: 50%) {
+// x-axis - left | center | right | length | %
+// y-axis - top | center | bottom | length | %
+// z-axis - length
+ @include prefixer(transform-origin, $axes, webkit moz ms o spec);
+}
+
+@mixin transform-style ($style: flat) {
+ @include prefixer(transform-style, $style, webkit moz ms o spec);
+}
diff --git a/docs/_static/css/bourbon/css3/_transition.scss b/docs/_static/css/bourbon/css3/_transition.scss
new file mode 100644
index 0000000..180cde6
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_transition.scss
@@ -0,0 +1,34 @@
+// Shorthand mixin. Supports multiple parentheses-deliminated values for each variable.
+// Example: @include transition (all, 2.0s, ease-in-out);
+// @include transition ((opacity, width), (1.0s, 2.0s), ease-in, (0, 2s));
+// @include transition ($property:(opacity, width), $delay: (1.5s, 2.5s));
+
+@mixin transition ($properties...) {
+ @if length($properties) >= 1 {
+ @include prefixer(transition, $properties, webkit moz spec);
+ }
+
+ @else {
+ $properties: all 0.15s ease-out 0;
+ @include prefixer(transition, $properties, webkit moz spec);
+ }
+}
+
+@mixin transition-property ($properties...) {
+ -webkit-transition-property: transition-property-names($properties, 'webkit');
+ -moz-transition-property: transition-property-names($properties, 'moz');
+ transition-property: transition-property-names($properties, false);
+}
+
+@mixin transition-duration ($times...) {
+ @include prefixer(transition-duration, $times, webkit moz spec);
+}
+
+@mixin transition-timing-function ($motions...) {
+// ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier()
+ @include prefixer(transition-timing-function, $motions, webkit moz spec);
+}
+
+@mixin transition-delay ($times...) {
+ @include prefixer(transition-delay, $times, webkit moz spec);
+}
diff --git a/docs/_static/css/bourbon/css3/_user-select.scss b/docs/_static/css/bourbon/css3/_user-select.scss
new file mode 100644
index 0000000..1380aa8
--- /dev/null
+++ b/docs/_static/css/bourbon/css3/_user-select.scss
@@ -0,0 +1,3 @@
+@mixin user-select($arg: none) {
+ @include prefixer(user-select, $arg, webkit moz ms spec);
+}
diff --git a/docs/_static/css/bourbon/functions/_compact.scss b/docs/_static/css/bourbon/functions/_compact.scss
new file mode 100644
index 0000000..871500e
--- /dev/null
+++ b/docs/_static/css/bourbon/functions/_compact.scss
@@ -0,0 +1,11 @@
+// Remove `false` values from a list
+
+@function compact($vars...) {
+ $list: ();
+ @each $var in $vars {
+ @if $var {
+ $list: append($list, $var, comma);
+ }
+ }
+ @return $list;
+}
diff --git a/docs/_static/css/bourbon/functions/_flex-grid.scss b/docs/_static/css/bourbon/functions/_flex-grid.scss
new file mode 100644
index 0000000..3bbd866
--- /dev/null
+++ b/docs/_static/css/bourbon/functions/_flex-grid.scss
@@ -0,0 +1,39 @@
+// Flexible grid
+@function flex-grid($columns, $container-columns: $fg-max-columns) {
+ $width: $columns * $fg-column + ($columns - 1) * $fg-gutter;
+ $container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter;
+ @return percentage($width / $container-width);
+}
+
+// Flexible gutter
+@function flex-gutter($container-columns: $fg-max-columns, $gutter: $fg-gutter) {
+ $container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter;
+ @return percentage($gutter / $container-width);
+}
+
+// The $fg-column, $fg-gutter and $fg-max-columns variables must be defined in your base stylesheet to properly use the flex-grid function.
+// This function takes the fluid grid equation (target / context = result) and uses columns to help define each.
+//
+// The calculation presumes that your column structure will be missing the last gutter:
+//
+// -- column -- gutter -- column -- gutter -- column
+//
+// $fg-column: 60px; // Column Width
+// $fg-gutter: 25px; // Gutter Width
+// $fg-max-columns: 12; // Total Columns For Main Container
+//
+// div {
+// width: flex-grid(4); // returns (315px / 995px) = 31.65829%;
+// margin-left: flex-gutter(); // returns (25px / 995px) = 2.51256%;
+//
+// p {
+// width: flex-grid(2, 4); // returns (145px / 315px) = 46.031746%;
+// float: left;
+// margin: flex-gutter(4); // returns (25px / 315px) = 7.936508%;
+// }
+//
+// blockquote {
+// float: left;
+// width: flex-grid(2, 4); // returns (145px / 315px) = 46.031746%;
+// }
+// }
\ No newline at end of file
diff --git a/docs/_static/css/bourbon/functions/_grid-width.scss b/docs/_static/css/bourbon/functions/_grid-width.scss
new file mode 100644
index 0000000..8e63d83
--- /dev/null
+++ b/docs/_static/css/bourbon/functions/_grid-width.scss
@@ -0,0 +1,13 @@
+@function grid-width($n) {
+ @return $n * $gw-column + ($n - 1) * $gw-gutter;
+}
+
+// The $gw-column and $gw-gutter variables must be defined in your base stylesheet to properly use the grid-width function.
+//
+// $gw-column: 100px; // Column Width
+// $gw-gutter: 40px; // Gutter Width
+//
+// div {
+// width: grid-width(4); // returns 520px;
+// margin-left: $gw-gutter; // returns 40px;
+// }
diff --git a/docs/_static/css/bourbon/functions/_linear-gradient.scss b/docs/_static/css/bourbon/functions/_linear-gradient.scss
new file mode 100644
index 0000000..c8454d8
--- /dev/null
+++ b/docs/_static/css/bourbon/functions/_linear-gradient.scss
@@ -0,0 +1,13 @@
+@function linear-gradient($pos, $gradients...) {
+ $type: linear;
+ $pos-type: type-of(nth($pos, 1));
+
+ // if $pos doesn't exist, fix $gradient
+ @if ($pos-type == color) or (nth($pos, 1) == "transparent") {
+ $gradients: zip($pos $gradients);
+ $pos: false;
+ }
+
+ $type-gradient: $type, $pos, $gradients;
+ @return $type-gradient;
+}
diff --git a/docs/_static/css/bourbon/functions/_modular-scale.scss b/docs/_static/css/bourbon/functions/_modular-scale.scss
new file mode 100644
index 0000000..dddccb5
--- /dev/null
+++ b/docs/_static/css/bourbon/functions/_modular-scale.scss
@@ -0,0 +1,40 @@
+@function modular-scale($value, $increment, $ratio) {
+ @if $increment > 0 {
+ @for $i from 1 through $increment {
+ $value: ($value * $ratio);
+ }
+ }
+
+ @if $increment < 0 {
+ $increment: abs($increment);
+ @for $i from 1 through $increment {
+ $value: ($value / $ratio);
+ }
+ }
+
+ @return $value;
+}
+
+// div {
+// Increment Up GR with positive value
+// font-size: modular-scale(14px, 1, 1.618); // returns: 22.652px
+//
+// Increment Down GR with negative value
+// font-size: modular-scale(14px, -1, 1.618); // returns: 8.653px
+//
+// Can be used with ceil(round up) or floor(round down)
+// font-size: floor( modular-scale(14px, 1, 1.618) ); // returns: 22px
+// font-size: ceil( modular-scale(14px, 1, 1.618) ); // returns: 23px
+// }
+//
+// modularscale.com
+
+@function golden-ratio($value, $increment) {
+ @return modular-scale($value, $increment, 1.618)
+}
+
+// div {
+// font-size: golden-ratio(14px, 1); // returns: 22.652px
+// }
+//
+// goldenratiocalculator.com
diff --git a/docs/_static/css/bourbon/functions/_px-to-em.scss b/docs/_static/css/bourbon/functions/_px-to-em.scss
new file mode 100644
index 0000000..2eb1031
--- /dev/null
+++ b/docs/_static/css/bourbon/functions/_px-to-em.scss
@@ -0,0 +1,8 @@
+// Convert pixels to ems
+// eg. for a relational value of 12px write em(12) when the parent is 16px
+// if the parent is another value say 24px write em(12, 24)
+
+@function em($pxval, $base: 16) {
+ @return ($pxval / $base) * 1em;
+}
+
diff --git a/docs/_static/css/bourbon/functions/_radial-gradient.scss b/docs/_static/css/bourbon/functions/_radial-gradient.scss
new file mode 100644
index 0000000..7558406
--- /dev/null
+++ b/docs/_static/css/bourbon/functions/_radial-gradient.scss
@@ -0,0 +1,23 @@
+// This function is required and used by the background-image mixin.
+@function radial-gradient($G1, $G2,
+ $G3: false, $G4: false,
+ $G5: false, $G6: false,
+ $G7: false, $G8: false,
+ $G9: false, $G10: false,
+ $pos: null,
+ $shape-size: null) {
+
+ $data: _radial-arg-parser($G1, $G2, $pos, $shape-size);
+ $G1: nth($data, 1);
+ $G2: nth($data, 2);
+ $pos: nth($data, 3);
+ $shape-size: nth($data, 4);
+
+ $type: radial;
+ $gradient: compact($G1, $G2, $G3, $G4, $G5, $G6, $G7, $G8, $G9, $G10);
+
+ $type-gradient: $type, $shape-size $pos, $gradient;
+ @return $type-gradient;
+}
+
+
diff --git a/docs/_static/css/bourbon/functions/_tint-shade.scss b/docs/_static/css/bourbon/functions/_tint-shade.scss
new file mode 100644
index 0000000..f717200
--- /dev/null
+++ b/docs/_static/css/bourbon/functions/_tint-shade.scss
@@ -0,0 +1,9 @@
+// Add percentage of white to a color
+@function tint($color, $percent){
+ @return mix(white, $color, $percent);
+}
+
+// Add percentage of black to a color
+@function shade($color, $percent){
+ @return mix(black, $color, $percent);
+}
diff --git a/docs/_static/css/bourbon/functions/_transition-property-name.scss b/docs/_static/css/bourbon/functions/_transition-property-name.scss
new file mode 100644
index 0000000..54cd422
--- /dev/null
+++ b/docs/_static/css/bourbon/functions/_transition-property-name.scss
@@ -0,0 +1,22 @@
+// Return vendor-prefixed property names if appropriate
+// Example: transition-property-names((transform, color, background), moz) -> -moz-transform, color, background
+//************************************************************************//
+@function transition-property-names($props, $vendor: false) {
+ $new-props: ();
+
+ @each $prop in $props {
+ $new-props: append($new-props, transition-property-name($prop, $vendor), comma);
+ }
+
+ @return $new-props;
+}
+
+@function transition-property-name($prop, $vendor: false) {
+ // put other properties that need to be prefixed here aswell
+ @if $vendor and $prop == transform {
+ @return unquote('-'+$vendor+'-'+$prop);
+ }
+ @else {
+ @return $prop;
+ }
+}
\ No newline at end of file
diff --git a/docs/_static/css/bourbon/helpers/_deprecated-webkit-gradient.scss b/docs/_static/css/bourbon/helpers/_deprecated-webkit-gradient.scss
new file mode 100644
index 0000000..cd17e28
--- /dev/null
+++ b/docs/_static/css/bourbon/helpers/_deprecated-webkit-gradient.scss
@@ -0,0 +1,39 @@
+// Render Deprecated Webkit Gradient - Linear || Radial
+//************************************************************************//
+@function _deprecated-webkit-gradient($type,
+ $deprecated-pos1, $deprecated-pos2,
+ $full,
+ $deprecated-radius1: false, $deprecated-radius2: false) {
+ $gradient-list: ();
+ $gradient: false;
+ $full-length: length($full);
+ $percentage: false;
+ $gradient-type: $type;
+
+ @for $i from 1 through $full-length {
+ $gradient: nth($full, $i);
+
+ @if length($gradient) == 2 {
+ $color-stop: color-stop(nth($gradient, 2), nth($gradient, 1));
+ $gradient-list: join($gradient-list, $color-stop, comma);
+ }
+ @else if $gradient != null {
+ @if $i == $full-length {
+ $percentage: 100%;
+ }
+ @else {
+ $percentage: ($i - 1) * (100 / ($full-length - 1)) + "%";
+ }
+ $color-stop: color-stop(unquote($percentage), $gradient);
+ $gradient-list: join($gradient-list, $color-stop, comma);
+ }
+ }
+
+ @if $type == radial {
+ $gradient: -webkit-gradient(radial, $deprecated-pos1, $deprecated-radius1, $deprecated-pos2, $deprecated-radius2, $gradient-list);
+ }
+ @else if $type == linear {
+ $gradient: -webkit-gradient(linear, $deprecated-pos1, $deprecated-pos2, $gradient-list);
+ }
+ @return $gradient;
+}
diff --git a/docs/_static/css/bourbon/helpers/_gradient-positions-parser.scss b/docs/_static/css/bourbon/helpers/_gradient-positions-parser.scss
new file mode 100644
index 0000000..07d30b6
--- /dev/null
+++ b/docs/_static/css/bourbon/helpers/_gradient-positions-parser.scss
@@ -0,0 +1,13 @@
+@function _gradient-positions-parser($gradient-type, $gradient-positions) {
+ @if $gradient-positions
+ and ($gradient-type == linear)
+ and (type-of($gradient-positions) != color) {
+ $gradient-positions: _linear-positions-parser($gradient-positions);
+ }
+ @else if $gradient-positions
+ and ($gradient-type == radial)
+ and (type-of($gradient-positions) != color) {
+ $gradient-positions: _radial-positions-parser($gradient-positions);
+ }
+ @return $gradient-positions;
+}
diff --git a/docs/_static/css/bourbon/helpers/_linear-positions-parser.scss b/docs/_static/css/bourbon/helpers/_linear-positions-parser.scss
new file mode 100644
index 0000000..d26383e
--- /dev/null
+++ b/docs/_static/css/bourbon/helpers/_linear-positions-parser.scss
@@ -0,0 +1,61 @@
+@function _linear-positions-parser($pos) {
+ $type: type-of(nth($pos, 1));
+ $spec: null;
+ $degree: null;
+ $side: null;
+ $corner: null;
+ $length: length($pos);
+ // Parse Side and corner positions
+ @if ($length > 1) {
+ @if nth($pos, 1) == "to" { // Newer syntax
+ $side: nth($pos, 2);
+
+ @if $length == 2 { // eg. to top
+ // Swap for backwards compatability
+ $degree: _position-flipper(nth($pos, 2));
+ }
+ @else if $length == 3 { // eg. to top left
+ $corner: nth($pos, 3);
+ }
+ }
+ @else if $length == 2 { // Older syntax ("top left")
+ $side: _position-flipper(nth($pos, 1));
+ $corner: _position-flipper(nth($pos, 2));
+ }
+
+ @if ("#{$side} #{$corner}" == "left top") or ("#{$side} #{$corner}" == "top left") {
+ $degree: _position-flipper(#{$side}) _position-flipper(#{$corner});
+ }
+ @else if ("#{$side} #{$corner}" == "right top") or ("#{$side} #{$corner}" == "top right") {
+ $degree: _position-flipper(#{$side}) _position-flipper(#{$corner});
+ }
+ @else if ("#{$side} #{$corner}" == "right bottom") or ("#{$side} #{$corner}" == "bottom right") {
+ $degree: _position-flipper(#{$side}) _position-flipper(#{$corner});
+ }
+ @else if ("#{$side} #{$corner}" == "left bottom") or ("#{$side} #{$corner}" == "bottom left") {
+ $degree: _position-flipper(#{$side}) _position-flipper(#{$corner});
+ }
+ $spec: to $side $corner;
+ }
+ @else if $length == 1 {
+ // Swap for backwards compatability
+ @if $type == string {
+ $degree: $pos;
+ $spec: to _position-flipper($pos);
+ }
+ @else {
+ $degree: -270 - $pos; //rotate the gradient opposite from spec
+ $spec: $pos;
+ }
+ }
+ $degree: unquote($degree + ",");
+ $spec: unquote($spec + ",");
+ @return $degree $spec;
+}
+
+@function _position-flipper($pos) {
+ @return if($pos == left, right, null)
+ if($pos == right, left, null)
+ if($pos == top, bottom, null)
+ if($pos == bottom, top, null);
+}
diff --git a/docs/_static/css/bourbon/helpers/_radial-arg-parser.scss b/docs/_static/css/bourbon/helpers/_radial-arg-parser.scss
new file mode 100644
index 0000000..3466695
--- /dev/null
+++ b/docs/_static/css/bourbon/helpers/_radial-arg-parser.scss
@@ -0,0 +1,69 @@
+@function _radial-arg-parser($G1, $G2, $pos, $shape-size) {
+ @each $value in $G1, $G2 {
+ $first-val: nth($value, 1);
+ $pos-type: type-of($first-val);
+ $spec-at-index: null;
+
+ // Determine if spec was passed to mixin
+ @if type-of($value) == list {
+ $spec-at-index: if(index($value, at), index($value, at), false);
+ }
+ @if $spec-at-index {
+ @if $spec-at-index > 1 {
+ @for $i from 1 through ($spec-at-index - 1) {
+ $shape-size: $shape-size nth($value, $i);
+ }
+ @for $i from ($spec-at-index + 1) through length($value) {
+ $pos: $pos nth($value, $i);
+ }
+ }
+ @else if $spec-at-index == 1 {
+ @for $i from ($spec-at-index + 1) through length($value) {
+ $pos: $pos nth($value, $i);
+ }
+ }
+ $G1: false;
+ }
+
+ // If not spec calculate correct values
+ @else {
+ @if ($pos-type != color) or ($first-val != "transparent") {
+ @if ($pos-type == number)
+ or ($first-val == "center")
+ or ($first-val == "top")
+ or ($first-val == "right")
+ or ($first-val == "bottom")
+ or ($first-val == "left") {
+
+ $pos: $value;
+
+ @if $pos == $G1 {
+ $G1: false;
+ }
+ }
+
+ @else if
+ ($first-val == "ellipse")
+ or ($first-val == "circle")
+ or ($first-val == "closest-side")
+ or ($first-val == "closest-corner")
+ or ($first-val == "farthest-side")
+ or ($first-val == "farthest-corner")
+ or ($first-val == "contain")
+ or ($first-val == "cover") {
+
+ $shape-size: $value;
+
+ @if $value == $G1 {
+ $G1: false;
+ }
+
+ @else if $value == $G2 {
+ $G2: false;
+ }
+ }
+ }
+ }
+ }
+ @return $G1, $G2, $pos, $shape-size;
+}
diff --git a/docs/_static/css/bourbon/helpers/_radial-positions-parser.scss b/docs/_static/css/bourbon/helpers/_radial-positions-parser.scss
new file mode 100644
index 0000000..6a5b477
--- /dev/null
+++ b/docs/_static/css/bourbon/helpers/_radial-positions-parser.scss
@@ -0,0 +1,18 @@
+@function _radial-positions-parser($gradient-pos) {
+ $shape-size: nth($gradient-pos, 1);
+ $pos: nth($gradient-pos, 2);
+ $shape-size-spec: _shape-size-stripper($shape-size);
+
+ $pre-spec: unquote(if($pos, "#{$pos}, ", null))
+ unquote(if($shape-size, "#{$shape-size},", null));
+ $pos-spec: if($pos, "at #{$pos}", null);
+
+ $spec: "#{$shape-size-spec} #{$pos-spec}";
+
+ // Add comma
+ @if ($spec != ' ') {
+ $spec: "#{$spec},"
+ }
+
+ @return $pre-spec $spec;
+}
diff --git a/docs/_static/css/bourbon/helpers/_render-gradients.scss b/docs/_static/css/bourbon/helpers/_render-gradients.scss
new file mode 100644
index 0000000..5765676
--- /dev/null
+++ b/docs/_static/css/bourbon/helpers/_render-gradients.scss
@@ -0,0 +1,26 @@
+// User for linear and radial gradients within background-image or border-image properties
+
+@function _render-gradients($gradient-positions, $gradients, $gradient-type, $vendor: false) {
+ $pre-spec: null;
+ $spec: null;
+ $vendor-gradients: null;
+ @if $gradient-type == linear {
+ @if $gradient-positions {
+ $pre-spec: nth($gradient-positions, 1);
+ $spec: nth($gradient-positions, 2);
+ }
+ }
+ @else if $gradient-type == radial {
+ $pre-spec: nth($gradient-positions, 1);
+ $spec: nth($gradient-positions, 2);
+ }
+
+ @if $vendor {
+ $vendor-gradients: -#{$vendor}-#{$gradient-type}-gradient(#{$pre-spec} $gradients);
+ }
+ @else if $vendor == false {
+ $vendor-gradients: "#{$gradient-type}-gradient(#{$spec} #{$gradients})";
+ $vendor-gradients: unquote($vendor-gradients);
+ }
+ @return $vendor-gradients;
+}
diff --git a/docs/_static/css/bourbon/helpers/_shape-size-stripper.scss b/docs/_static/css/bourbon/helpers/_shape-size-stripper.scss
new file mode 100644
index 0000000..ee5eda4
--- /dev/null
+++ b/docs/_static/css/bourbon/helpers/_shape-size-stripper.scss
@@ -0,0 +1,10 @@
+@function _shape-size-stripper($shape-size) {
+ $shape-size-spec: null;
+ @each $value in $shape-size {
+ @if ($value == "cover") or ($value == "contain") {
+ $value: null;
+ }
+ $shape-size-spec: "#{$shape-size-spec} #{$value}";
+ }
+ @return $shape-size-spec;
+}
diff --git a/docs/_static/css/neat/_neat-helpers.scss b/docs/_static/css/neat/_neat-helpers.scss
new file mode 100644
index 0000000..86021b1
--- /dev/null
+++ b/docs/_static/css/neat/_neat-helpers.scss
@@ -0,0 +1,8 @@
+// Functions
+@import "functions/private";
+@import "functions/new-breakpoint";
+@import "functions/px-to-em";
+
+// Settings
+@import "settings/grid";
+@import "settings/visual-grid";
diff --git a/docs/_static/css/neat/_neat.scss b/docs/_static/css/neat/_neat.scss
new file mode 100644
index 0000000..cb5876b
--- /dev/null
+++ b/docs/_static/css/neat/_neat.scss
@@ -0,0 +1,21 @@
+// Bourbon Neat
+// MIT Licensed
+// Copyright (c) 2012-2013 thoughtbot, inc.
+
+// Helpers
+@import "neat-helpers";
+
+// Grid
+@import "grid/private";
+@import "grid/reset";
+@import "grid/grid";
+@import "grid/omega";
+@import "grid/outer-container";
+@import "grid/span-columns";
+@import "grid/row";
+@import "grid/shift";
+@import "grid/pad";
+@import "grid/fill-parent";
+@import "grid/media";
+@import "grid/to-deprecate";
+@import "grid/visual-grid";
diff --git a/docs/_static/css/neat/functions/_new-breakpoint.scss b/docs/_static/css/neat/functions/_new-breakpoint.scss
new file mode 100644
index 0000000..d89dcd1
--- /dev/null
+++ b/docs/_static/css/neat/functions/_new-breakpoint.scss
@@ -0,0 +1,16 @@
+@function new-breakpoint($query:$feature $value $columns, $total-columns: $grid-columns) {
+
+ @if length($query) == 1 {
+ $query: $default-feature nth($query, 1) $total-columns;
+ }
+
+ @else if length($query) == 2 or length($query) == 4 {
+ $query: append($query, $total-columns);
+ }
+
+ @if not belongs-to($query, $visual-grid-breakpoints) {
+ $visual-grid-breakpoints: append($visual-grid-breakpoints, $query, comma);
+ }
+
+ @return $query;
+}
diff --git a/docs/_static/css/neat/functions/_private.scss b/docs/_static/css/neat/functions/_private.scss
new file mode 100644
index 0000000..49683d7
--- /dev/null
+++ b/docs/_static/css/neat/functions/_private.scss
@@ -0,0 +1,107 @@
+// Checks if a number is even
+@function is-even($int) {
+ @if $int%2 == 0 {
+ @return true;
+ }
+
+ @return false;
+}
+
+// Checks if an element belongs to a list
+@function belongs-to($tested-item, $list) {
+ @each $item in $list {
+ @if $item == $tested-item {
+ @return true;
+ }
+ }
+
+ @return false;
+}
+
+// Contains display value
+@function contains-display-value($query) {
+ @if belongs-to(table, $query) or belongs-to(block, $query) or belongs-to(inline-block, $query) or belongs-to(inline, $query) {
+ @return true;
+ }
+
+ @return false;
+}
+
+// Parses the first argument of span-columns()
+@function container-span($span: $span) {
+ @if length($span) == 3 {
+ $container-columns: nth($span, 3);
+ @return $container-columns;
+ }
+
+ @else if length($span) == 2 {
+ $container-columns: nth($span, 2);
+ @return $container-columns;
+ }
+
+ @else {
+ @return $grid-columns;
+ }
+}
+
+// Generates a striped background
+@function gradient-stops($grid-columns, $color: $visual-grid-color) {
+ $transparent: rgba(0,0,0,0);
+
+ $column-width: flex-grid(1, $grid-columns);
+ $gutter-width: flex-gutter($grid-columns);
+ $column-offset: $column-width;
+
+ $values: ($transparent 0, $color 0);
+
+ @for $i from 1 to $grid-columns*2 {
+ @if is-even($i) {
+ $values: append($values, $transparent $column-offset, comma);
+ $values: append($values, $color $column-offset, comma);
+ $column-offset: $column-offset + $column-width;
+ }
+
+ @else {
+ $values: append($values, $color $column-offset, comma);
+ $values: append($values, $transparent $column-offset, comma);
+ $column-offset: $column-offset + $gutter-width;
+ }
+ }
+
+ @return $values;
+}
+
+// Layout direction
+@function get-direction($layout, $default) {
+ $direction: nil;
+
+ @if $layout == LTR or $layout == RTL {
+ $direction: direction-from-layout($layout);
+ } @else {
+ $direction: direction-from-layout($default);
+ }
+
+ @return $direction;
+}
+
+@function direction-from-layout($layout) {
+ $direction: nil;
+
+ @if $layout == LTR {
+ $direction: right;
+ } @else {
+ $direction: left;
+ }
+
+ @return $direction;
+}
+
+@function get-opposite-direction($direction) {
+ $opposite-direction: left;
+
+ @if $direction == left {
+ $opposite-direction: right;
+ }
+
+ @return $opposite-direction;
+}
diff --git a/docs/_static/css/neat/functions/_px-to-em.scss b/docs/_static/css/neat/functions/_px-to-em.scss
new file mode 100644
index 0000000..058e51e
--- /dev/null
+++ b/docs/_static/css/neat/functions/_px-to-em.scss
@@ -0,0 +1,3 @@
+@function em($pxval, $base: 16) {
+ @return ($pxval / $base) * 1em;
+}
diff --git a/docs/_static/css/neat/grid/_fill-parent.scss b/docs/_static/css/neat/grid/_fill-parent.scss
new file mode 100644
index 0000000..859c977
--- /dev/null
+++ b/docs/_static/css/neat/grid/_fill-parent.scss
@@ -0,0 +1,7 @@
+@mixin fill-parent() {
+ width: 100%;
+
+ @if $border-box-sizing == false {
+ @include box-sizing(border-box);
+ }
+}
diff --git a/docs/_static/css/neat/grid/_grid.scss b/docs/_static/css/neat/grid/_grid.scss
new file mode 100644
index 0000000..e074b6c
--- /dev/null
+++ b/docs/_static/css/neat/grid/_grid.scss
@@ -0,0 +1,5 @@
+@if $border-box-sizing == true {
+ * {
+ @include box-sizing(border-box);
+ }
+}
diff --git a/docs/_static/css/neat/grid/_media.scss b/docs/_static/css/neat/grid/_media.scss
new file mode 100644
index 0000000..7c9872f
--- /dev/null
+++ b/docs/_static/css/neat/grid/_media.scss
@@ -0,0 +1,51 @@
+@mixin media($query:$feature $value $columns, $total-columns: $grid-columns) {
+
+ @if length($query) == 1 {
+ @media screen and ($default-feature: nth($query, 1)) {
+ $default-grid-columns: $grid-columns;
+ $grid-columns: $total-columns;
+ @content;
+ $grid-columns: $default-grid-columns;
+ }
+ }
+
+ @else if length($query) == 2 {
+ @media screen and (nth($query, 1): nth($query, 2)) {
+ $default-grid-columns: $grid-columns;
+ $grid-columns: $total-columns;
+ @content;
+ $grid-columns: $default-grid-columns;
+ }
+ }
+
+ @else if length($query) == 3 {
+ @media screen and (nth($query, 1): nth($query, 2)) {
+ $default-grid-columns: $grid-columns;
+ $grid-columns: nth($query, 3);
+ @content;
+ $grid-columns: $default-grid-columns;
+ }
+ }
+
+ @else if length($query) == 4 {
+ @media screen and (nth($query, 1): nth($query, 2)) and (nth($query, 3): nth($query, 4)) {
+ $default-grid-columns: $grid-columns;
+ $grid-columns: $total-columns;
+ @content;
+ $grid-columns: $default-grid-columns;
+ }
+ }
+
+ @else if length($query) == 5 {
+ @media screen and (nth($query, 1): nth($query, 2)) and (nth($query, 3): nth($query, 4)) {
+ $default-grid-columns: $grid-columns;
+ $grid-columns: nth($query, 5);
+ @content;
+ $grid-columns: $default-grid-columns;
+ }
+ }
+
+ @else {
+ @warn "Wrong number of arguments for breakpoint(). Read the documentation for more details.";
+ }
+}
diff --git a/docs/_static/css/neat/grid/_omega.scss b/docs/_static/css/neat/grid/_omega.scss
new file mode 100644
index 0000000..902459b
--- /dev/null
+++ b/docs/_static/css/neat/grid/_omega.scss
@@ -0,0 +1,79 @@
+// Remove last element gutter
+@mixin omega($query: block, $direction: default) {
+ $table: if(belongs-to(table, $query), true, false);
+ $auto: if(belongs-to(auto, $query), true, false);
+
+ @if $direction != default {
+ @warn "The omega mixin will no longer take a $direction argument. To change the layout direction, use row($direction) or set $default-layout-direction instead."
+ } @else {
+ $direction: get-direction($layout-direction, $default-layout-direction);
+ }
+
+ @if length($query) == 1 {
+ @if $auto {
+ &:last-child {
+ margin-#{$direction}: 0;
+ }
+ }
+
+ @else if contains-display-value($query) {
+ @if $table {
+ padding-#{$direction}: 0;
+ }
+
+ @else {
+ margin-#{$direction}: 0;
+ }
+ }
+
+ @else {
+ @include nth-child($query, $direction);
+ }
+ }
+
+ @else if length($query) == 2 {
+ @if $table {
+ @if $auto {
+ &:last-child {
+ padding-#{$direction}: 0;
+ }
+ }
+
+ @else {
+ &:nth-child(#{nth($query, 1)}) {
+ padding-#{$direction}: 0;
+ }
+ }
+ }
+
+ @else {
+ @if $auto {
+ &:last-child {
+ margin-#{$direction}: 0;
+ }
+ }
+
+ @else {
+ @include nth-child(nth($query, 1), $direction);
+ }
+ }
+ }
+
+ @else {
+ @warn "Too many arguments passed to the omega() mixin."
+ }
+}
+
+@mixin nth-child($query, $direction) {
+ $opposite-direction: get-opposite-direction($direction);
+
+ &:nth-child(#{$query}) {
+ margin-#{$direction}: 0;
+ }
+
+ @if type-of($query) == number {
+ &:nth-child(#{$query}+1) {
+ clear: $opposite-direction;
+ }
+ }
+}
diff --git a/docs/_static/css/neat/grid/_outer-container.scss b/docs/_static/css/neat/grid/_outer-container.scss
new file mode 100644
index 0000000..22c541f
--- /dev/null
+++ b/docs/_static/css/neat/grid/_outer-container.scss
@@ -0,0 +1,8 @@
+@mixin outer-container {
+ @include clearfix;
+ max-width: $max-width;
+ margin: {
+ left: auto;
+ right: auto;
+ }
+}
diff --git a/docs/_static/css/neat/grid/_pad.scss b/docs/_static/css/neat/grid/_pad.scss
new file mode 100644
index 0000000..3ef5d80
--- /dev/null
+++ b/docs/_static/css/neat/grid/_pad.scss
@@ -0,0 +1,8 @@
+@mixin pad($padding: flex-gutter()) {
+ $padding-list: null;
+ @each $value in $padding {
+ $value: if($value == 'default', flex-gutter(), $value);
+ $padding-list: join($padding-list, $value);
+ }
+ padding: $padding-list;
+}
diff --git a/docs/_static/css/neat/grid/_private.scss b/docs/_static/css/neat/grid/_private.scss
new file mode 100644
index 0000000..38d3fce
--- /dev/null
+++ b/docs/_static/css/neat/grid/_private.scss
@@ -0,0 +1,50 @@
+$parent-columns: $grid-columns !default;
+$fg-column: $column;
+$fg-gutter: $gutter;
+$fg-max-columns: $grid-columns;
+$container-display-table: false !default;
+$layout-direction: nil !default;
+
+@function flex-grid($columns, $container-columns: $fg-max-columns) {
+ $width: $columns * $fg-column + ($columns - 1) * $fg-gutter;
+ $container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter;
+ @return percentage($width / $container-width);
+}
+
+@function flex-gutter($container-columns: $fg-max-columns, $gutter: $fg-gutter) {
+ $container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter;
+ @return percentage($gutter / $container-width);
+}
+
+@function grid-width($n) {
+ @return $n * $gw-column + ($n - 1) * $gw-gutter;
+}
+
+@function get-parent-columns($columns) {
+ @if $columns != $grid-columns {
+ $parent-columns: $columns;
+ } @else {
+ $parent-columns: $grid-columns;
+ }
+
+ @return $parent-columns;
+}
+
+@function is-display-table($container-is-display-table, $display) {
+ $display-table: false;
+
+ @if $container-is-display-table == true {
+ $display-table: true;
+ } @else if $display == table {
+ $display-table: true;
+ }
+
+ @return $display-table;
+}
+
+@function get-padding-for-table-layout($columns, $total-columns) {
+ $total-padding: flex-gutter($total-columns) * ($columns - 1);
+ $padding: $total-padding / $columns;
+
+ @return $padding;
+}
diff --git a/docs/_static/css/neat/grid/_reset.scss b/docs/_static/css/neat/grid/_reset.scss
new file mode 100644
index 0000000..f670019
--- /dev/null
+++ b/docs/_static/css/neat/grid/_reset.scss
@@ -0,0 +1,12 @@
+@mixin reset-display {
+ $container-display-table: false;
+}
+
+@mixin reset-layout-direction {
+ $layout-direction: $default-layout-direction;
+}
+
+@mixin reset-all {
+ @include reset-display;
+ @include reset-layout-direction;
+}
diff --git a/docs/_static/css/neat/grid/_row.scss b/docs/_static/css/neat/grid/_row.scss
new file mode 100644
index 0000000..582603d
--- /dev/null
+++ b/docs/_static/css/neat/grid/_row.scss
@@ -0,0 +1,17 @@
+@mixin row($display: block, $direction: $default-layout-direction) {
+ @include clearfix;
+ $layout-direction: $direction;
+
+ @if $display == table {
+ display: table;
+ @include fill-parent;
+ table-layout: fixed;
+ $container-display-table: true;
+ }
+
+ @else {
+ display: block;
+ $container-display-table: false;
+ }
+}
+
diff --git a/docs/_static/css/neat/grid/_shift.scss b/docs/_static/css/neat/grid/_shift.scss
new file mode 100644
index 0000000..b3cb503
--- /dev/null
+++ b/docs/_static/css/neat/grid/_shift.scss
@@ -0,0 +1,9 @@
+@mixin shift($n-columns: 1) {
+ $direction: get-direction($layout-direction, $default-layout-direction);
+ $opposite-direction: get-opposite-direction($direction);
+
+ margin-#{$opposite-direction}: $n-columns * flex-grid(1, $parent-columns) + $n-columns * flex-gutter($parent-columns);
+
+ // Reset nesting context
+ $parent-columns: $grid-columns;
+}
diff --git a/docs/_static/css/neat/grid/_span-columns.scss b/docs/_static/css/neat/grid/_span-columns.scss
new file mode 100644
index 0000000..609c349
--- /dev/null
+++ b/docs/_static/css/neat/grid/_span-columns.scss
@@ -0,0 +1,38 @@
+@mixin span-columns($span: $columns of $container-columns, $display: block) {
+ $columns: nth($span, 1);
+ $container-columns: container-span($span);
+
+ // Set nesting context (used by shift())
+ $parent-columns: get-parent-columns($container-columns);
+
+ $direction: get-direction($layout-direction, $default-layout-direction);
+ $opposite-direction: get-opposite-direction($direction);
+
+ $display-table: is-display-table($container-display-table, $display);
+
+ @if $display-table {
+ $padding: get-padding-for-table-layout($columns, $container-columns);
+ display: table-cell;
+ padding-#{$direction}: $padding;
+ width: flex-grid($columns, $container-columns) + $padding;
+ } @else {
+ display: block;
+ float: #{$opposite-direction};
+
+ @if $display == collapse {
+ width: flex-grid($columns, $container-columns) + flex-gutter($container-columns);
+
+ &:last-child {
+ width: flex-grid($columns, $container-columns);
+ }
+
+ } @else {
+ margin-#{$direction}: flex-gutter($container-columns);
+ width: flex-grid($columns, $container-columns);
+
+ &:last-child {
+ margin-#{$direction}: 0;
+ }
+ }
+ }
+}
diff --git a/docs/_static/css/neat/grid/_to-deprecate.scss b/docs/_static/css/neat/grid/_to-deprecate.scss
new file mode 100644
index 0000000..d0a681f
--- /dev/null
+++ b/docs/_static/css/neat/grid/_to-deprecate.scss
@@ -0,0 +1,57 @@
+@mixin breakpoint($query:$feature $value $columns, $total-columns: $grid-columns) {
+ @warn "The breakpoint() mixin was renamed to media() in Neat 1.0. Please update your project with the new syntax before the next version bump.";
+
+ @if length($query) == 1 {
+ @media screen and ($default-feature: nth($query, 1)) {
+ $default-grid-columns: $grid-columns;
+ $grid-columns: $total-columns;
+ @content;
+ $grid-columns: $default-grid-columns;
+ }
+ }
+
+ @else if length($query) == 2 {
+ @media screen and (nth($query, 1): nth($query, 2)) {
+ $default-grid-columns: $grid-columns;
+ $grid-columns: $total-columns;
+ @content;
+ $grid-columns: $default-grid-columns;
+ }
+ }
+
+ @else if length($query) == 3 {
+ @media screen and (nth($query, 1): nth($query, 2)) {
+ $default-grid-columns: $grid-columns;
+ $grid-columns: nth($query, 3);
+ @content;
+ $grid-columns: $default-grid-columns;
+ }
+ }
+
+ @else if length($query) == 4 {
+ @media screen and (nth($query, 1): nth($query, 2)) and (nth($query, 3): nth($query, 4)) {
+ $default-grid-columns: $grid-columns;
+ $grid-columns: $total-columns;
+ @content;
+ $grid-columns: $default-grid-columns;
+ }
+ }
+
+ @else if length($query) == 5 {
+ @media screen and (nth($query, 1): nth($query, 2)) and (nth($query, 3): nth($query, 4)) {
+ $default-grid-columns: $grid-columns;
+ $grid-columns: nth($query, 5);
+ @content;
+ $grid-columns: $default-grid-columns;
+ }
+ }
+
+ @else {
+ @warn "Wrong number of arguments for breakpoint(). Read the documentation for more details.";
+ }
+}
+
+@mixin nth-omega($nth, $display: block, $direction: default) {
+ @warn "The nth-omega() mixin is deprecated. Please use omega() instead.";
+ @include omega($nth $display, $direction);
+}
diff --git a/docs/_static/css/neat/grid/_visual-grid.scss b/docs/_static/css/neat/grid/_visual-grid.scss
new file mode 100644
index 0000000..1c822fd
--- /dev/null
+++ b/docs/_static/css/neat/grid/_visual-grid.scss
@@ -0,0 +1,41 @@
+@mixin grid-column-gradient($values...) {
+ background-image: deprecated-webkit-gradient(linear, left top, left bottom, $values);
+ background-image: -webkit-linear-gradient(left, $values);
+ background-image: -moz-linear-gradient(left, $values);
+ background-image: -ms-linear-gradient(left, $values);
+ background-image: -o-linear-gradient(left, $values);
+ background-image: unquote("linear-gradient(left, #{$values})");
+}
+
+@if $visual-grid == true or $visual-grid == yes {
+ body:before {
+ content: '';
+ display: inline-block;
+ @include grid-column-gradient(gradient-stops($grid-columns));
+ height: 100%;
+ left: 0;
+ margin: 0 auto;
+ max-width: $max-width;
+ opacity: $visual-grid-opacity;
+ position: fixed;
+ right: 0;
+ width: 100%;
+ pointer-events: none;
+
+ @if $visual-grid-index == back {
+ z-index: -1;
+ }
+
+ @else if $visual-grid-index == front {
+ z-index: 9999;
+ }
+
+ @each $breakpoint in $visual-grid-breakpoints {
+ @if $breakpoint != nil {
+ @include media($breakpoint) {
+ @include grid-column-gradient(gradient-stops($grid-columns));
+ }
+ }
+ }
+ }
+}
diff --git a/docs/_static/css/neat/settings/_grid.scss b/docs/_static/css/neat/settings/_grid.scss
new file mode 100644
index 0000000..f1dcda4
--- /dev/null
+++ b/docs/_static/css/neat/settings/_grid.scss
@@ -0,0 +1,7 @@
+$column: golden-ratio(1em, 3) !default; // Column width
+$gutter: golden-ratio(1em, 1) !default; // Gutter between each two columns
+$grid-columns: 12 !default; // Total number of columns in the grid
+$max-width: em(1088) !default; // Max-width of the outer container
+$border-box-sizing: true !default; // Makes all elements have a border-box layout
+$default-feature: min-width; // Default @media feature for the breakpoint() mixin
+$default-layout-direction: LTR !default;
diff --git a/docs/_static/css/neat/settings/_visual-grid.scss b/docs/_static/css/neat/settings/_visual-grid.scss
new file mode 100644
index 0000000..611c2b3
--- /dev/null
+++ b/docs/_static/css/neat/settings/_visual-grid.scss
@@ -0,0 +1,5 @@
+$visual-grid: false !default; // Display the base grid
+$visual-grid-color: #EEE !default;
+$visual-grid-index: back !default; // Show grid behind content (back) or overlay it over the content (front)
+$visual-grid-opacity: 0.4 !default;
+$visual-grid-breakpoints: () !default;
diff --git a/docs/_static/css/site.css b/docs/_static/css/site.css
new file mode 100644
index 0000000..8e0383a
--- /dev/null
+++ b/docs/_static/css/site.css
@@ -0,0 +1,118 @@
+@import url(http://fonts.googleapis.com/css?family=Open+Sans);
+* {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box; }
+
+* {
+ margin: 0;
+ padding: 0; }
+
+body {
+ background-color: green;
+ font-family: Open Sans, sans-serif; }
+
+.wrapper, main {
+ margin: 0 auto;
+ max-width: 960px; }
+
+nav {
+ *zoom: 1;
+ max-width: 68em;
+ margin-left: auto;
+ margin-right: auto;
+ border-bottom: solid black 1px;
+ height: 80px;
+ max-width: none;
+ color: #f2efe3; }
+ nav:before, nav:after {
+ content: " ";
+ display: table; }
+ nav:after {
+ clear: both; }
+ nav header {
+ display: block;
+ float: left;
+ margin-right: 3.57866%;
+ width: 48.21067%;
+ text-align: left; }
+ nav header:last-child {
+ margin-right: 0; }
+ nav ul {
+ display: block;
+ float: left;
+ margin-right: 3.57866%;
+ width: 48.21067%;
+ margin-top: 30px; }
+ nav ul:last-child {
+ margin-right: 0; }
+ nav ul li {
+ display: block;
+ float: right;
+ margin-left: 2em; }
+ nav a {
+ color: #f2efe3;
+ font-weight: 400;
+ text-decoration: none; }
+
+h1, h2, h3, h4, h5, h6 {
+ margin: 0 0 20px; }
+
+p, ul, ol, table, pre, dl {
+ margin: 0 0 20px; }
+
+h1, h2, h3 {
+ line-height: 1.1; }
+
+h1 {
+ font-size: 28px; }
+
+a small {
+ font-size: 11px;
+ margin-top: -0.6em;
+ display: block; }
+
+main {
+ *zoom: 1;
+ max-width: 68em;
+ margin-left: auto;
+ margin-right: auto;
+ max-width: 840px;
+ padding: 17px 30px 20px 30px;
+ background-color: #fbfbfb;
+ border: 1px solid #c5c1b4;
+ font-size: 14px;
+ color: #585755;
+ line-height: 1.75em; }
+ main:before, main:after {
+ content: " ";
+ display: table; }
+ main:after {
+ clear: both; }
+ main #left {
+ display: block;
+ float: left;
+ margin-right: 3.57866%;
+ width: 61.158%; }
+ main #left:last-child {
+ margin-right: 0; }
+ main #left figure {
+ text-align: center; }
+ main #left figure img {
+ margin: 0 auto; }
+ main #left #demo {
+ margin-top: 20px; }
+ main #right {
+ display: block;
+ float: left;
+ margin-right: 3.57866%;
+ width: 35.26334%; }
+ main #right:last-child {
+ margin-right: 0; }
+ main #right ul {
+ margin-left: 20px; }
+ main #right pre {
+ font-size: 0.90em; }
+ main #right #features > ul > li strong {
+ color: #222;
+ font-weight: 700; }
diff --git a/docs/_static/css/site.scss b/docs/_static/css/site.scss
new file mode 100644
index 0000000..669c4f9
--- /dev/null
+++ b/docs/_static/css/site.scss
@@ -0,0 +1,325 @@
+@import "bourbon/bourbon";
+@import "neat/neat";
+
+$blue: #00aac2;
+$text: #5c5c5c;
+
+* {
+ margin: 0;
+ padding: 0;
+}
+
+html, body {
+ height: 100%;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ font-weight: normal;
+ font-style: normal;
+}
+
+body {
+ font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
+ color: black;
+}
+
+.wrapper {
+ min-height: 100%;
+ height: auto !important;
+ height: 100%;
+ margin: 0 auto -2em;
+}
+
+.push, .footer {
+ height: 2em;
+
+ a, a:visited {
+ color: $blue;
+ text-decoration: none;
+ }
+}
+
+.header {
+ @include outer-container;
+ padding-top: 1em;
+ padding-bottom: 1em;
+
+ a, a:visited {
+ color: #4d4c4c;
+ text-decoration: none;
+ }
+
+ header {
+ @include span-columns(1 of 2);
+
+ font-weight: normal;
+
+ .logo {
+ max-height: 60px;
+ padding-right: 12px;
+ float: left;
+ }
+
+ h1 {
+ font-size: 1.55em;
+ margin-bottom: 0.3em;
+ }
+
+ h2 {
+ font-size: 1.05em;
+ }
+ }
+
+ nav {
+ @include span-columns(1 of 2);
+
+ ul {
+ padding-top: 1.55em + 0.3em;
+
+ li {
+ display: block;
+ float: right;
+ margin-left: 2em;
+
+ font-weight: 300;
+ text-transform: uppercase;
+
+ color: #444;
+ letter-spacing: 0.05em;
+ }
+ }
+ }
+}
+
+.outer {
+ background-color: rgb(238, 238, 238);
+ box-shadow: inset 0 0 0.5em #c0c0c0;
+
+ header {
+ @include outer-container;
+ padding: 1em 0;
+
+ h1 {
+ color: #555;
+ font: 300 35px Lato, "Helvetica Neue", Helvetica, Arial, sans-serif;
+ text-shadow: 1px 0 #c0c0c0;
+ letter-spacing: 0.05em;
+ }
+ }
+
+ footer {
+ @include outer-container;
+ }
+
+ .index {
+ @include outer-container;
+ padding: 1em;
+
+ figure {
+ @include span-columns(2 of 5);
+
+ text-align: center;
+ max-width: 300px;
+
+ img {
+ max-width: 100%;
+ max-height: 100%;
+ }
+ }
+
+ ul {
+ @include span-columns(3 of 5);
+ margin-top: 1em;
+
+ li {
+ list-style-type: none;
+ margin-bottom: 2em;
+
+ strong {
+ font-weight: normal;
+ font-size: 18px;
+ color: black;
+ }
+
+ p {
+ color: $text;
+ }
+
+ p + p {
+ margin-top: 0.5em;
+ }
+ }
+ }
+ }
+}
+
+.footer {
+ text-align: right;
+ padding: 0.5em 0;
+}
+
+main {
+ @include outer-container;
+ margin-top: 1.6em;
+ margin-bottom: 2em;
+ line-height: 1.6em;
+
+ h2:before {
+ content: "» ";
+ }
+
+ .links {
+ @include span-columns(1 of 3);
+
+ h2 {
+ margin-bottom: 1em;
+ margin-top: 1em;
+ }
+
+ > h2:first-child {
+ margin-top: 0;
+ }
+
+ li {
+ list-style-type: none;
+ }
+
+ .search {
+ input {
+ padding: 0.1em 0.4em;
+ }
+ }
+ }
+
+ .demo {
+ @include span-columns(2 of 3);
+
+ h2 {
+ margin-bottom: 1em;
+ }
+
+ h4 {
+ margin-bottom: 0.5em;
+ }
+ }
+
+ .sidebar {
+ @include span-columns(1 of 5);
+ display: block;
+
+ strong {
+ color: $blue;
+ font-weight: bold;
+ text-transform: uppercase;
+ }
+
+ ul {
+ margin-top: 0.5em;
+ margin-bottom: 1em;
+ }
+
+ ul:last-child {
+ margin-bottom: 0;
+ }
+
+ li {
+ border-left: solid 2px #d3d3d3;
+ margin-left: 0.25em;
+ padding-left: 0.75em;
+
+ padding-top: 0.25em;
+ padding-bottom: 0.25em;
+
+ font-weight: 300;
+ list-style-type: none;
+
+ a {
+ color: $text;
+ }
+ }
+
+ .active {
+ border-left-color: $blue;
+
+ a {
+ color: $blue;
+ font-weight: bold;
+ }
+ }
+ }
+
+ .docs {
+ @include span-columns(4 of 5);
+
+ font-size: 15px;
+ color: $text;
+
+ h2, h3, h4 {
+ margin-top: 1em;
+ margin-bottom: 1em;
+
+ }
+
+ h2 {
+ font-size: 24px;
+ }
+
+ h3 {
+ font-size: 18px;
+ }
+
+ h4 {
+ font-size: 14px;
+ }
+
+ pre, code {
+ color: rgb(77, 77, 76);
+ font-size: 12px;
+ font-family: Monaco, Menlo, Consolas, monospaced;
+ }
+
+ .highlight {
+ margin: 1.2em 0;
+ padding: 10px 15px;
+
+ background-color: rgb(238, 238, 238);
+ color: rgb(77, 77, 76);
+
+ border: 1px solid rgb(221, 221, 221);
+ border-radius: 4px;
+ overflow: auto;
+ }
+
+ p + p {
+ margin-top: 1em;
+ }
+
+ p + p:last-child {
+ margin-bottom: 0;
+ }
+
+ ul {
+
+ margin: 1em 0;
+
+ li {
+ list-style-type: square;
+ margin-left: 2em;
+ margin-right: 2em;
+ }
+ }
+ }
+
+ a {
+ text-decoration: none;
+ color: $blue;
+ }
+
+ dt {
+ font-weight: bold;
+ margin: 0.4em 0;
+ }
+ dd {
+ margin-left: 1.2em;
+ }
+}
diff --git a/docs/_static/duty_calls.png b/docs/_static/duty_calls.png
new file mode 100644
index 0000000000000000000000000000000000000000..4683d638b90e2b1820922757a6b63fe5b50a30cd
GIT binary patch
literal 14103
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⪼SofR=zjYOWERn5N5?(b=_^5rm(fD;5!nw--p|
zZ9BGbcPuUKsL@lyIXet}6^Ap1^%+j>m+OQuGha;92yarzEvQVh(;YJHX$lUDRL(sz
z*Z=uM`^p@Y5M7@=Uz)1fn!3?eGH)lGN5uPvqD@``
z_(chQ?lvw?uF788-e1L;*>SR!(9xKuH3l}WQ+CedZa+i)IwZycycOg{OhpKbOmMIm
zyFkUAD96c2&(i#!XJ%B>sf=`=5-OvAizYt-5!?hZQjIrT%Sc+9jZH@Sh;VH}A98s}
z8<%Jm{1ddy+gPvH^6x-*Sf=1Te!?U5%bWQ>r8MTc!H*~L>3A^aS=LOz$EHanxts>e
z80vsgtUaeLiwHPijU#j2NF`4XI0t>OuPJ5}?zAlO=>VJz5!>cHMVoYyENoq-jT*R4
znW+DfRQJ9yP!LJd&AwqPC!?s@ON&KX@oP9NC9Z>dpPqJhK1Ng#8i+LBKaIZ>A_ZD=
zX%`b+3g(pOUI@7?Zd8|!iQqTET}bkSgLGOtmh{JYuq--;)J)UJBXH>fF^#q-_AXV<<^=hmAUyXvCTu+r6TbjF_tGBk$U8yrN3@P2b`c&?NBreGR>f|Owga!
zNzS|K#{LmbW!yIAInrx?6|uY37Dh#`y^lu~$+>}bl!94quXUQ`%^wJFyYpF3G`JTr
zg4W;~q@gNzQ+DyN!btc^1F*m@3n!V~<4cNiUA9r#_8HQTnR7z4p>vE`U+isnV33wn
zgJZMueUT#cR#;bCz)V)~eEwHRzG4_o=XZlFw-#7EW*w{SOO1hcNNT*^5ZR)g&hK%1
zoRVlZgqP3PcqK5V$)cRhhA){ND5)IAzudQh_U;oyrhE<%l*JEwpX)<`HT}~IDigFr
zCv1}|SQ`a*>$61ojJ3&feZgy_PGJp3Nqke>0GcB_1X^jg1#BeG;h!K8L19Pw?skEW
zniTu1gZwvBam5ca?n9$;C}qZ2;C&;{Pwd5#RgQ%vW~UoX4#c*YH;96cWaNANTk@ty
zIqzH74?h(~RUP9UUz}({z-0Or04(==dPF)xDD{>PLQis1zA@;+>t`QPV21QzTc3qAc`CFW*iuh4L4C~
zi1cNFXCyMdZ(>|B-zcGZbIbQ2o>yPCmjhz!2DkWnQSFG!|MSqj?^N^hFOYm!hH~hQ
z;A6(YxAg;=&>to}auSxWFah`~OizJxKS?BSTTk(UUyp2WH+N-@FUIr4vcdU0t2c6z
zdEQzT#XNQZaY0(T&-HIeY&GBp5}b70C1E#8jsNg9<#J9L{4Jmt;cdidU;!VY-s;L$
zk~rT$kN?nS>D<(4)lPCO#Hr8{>
zg=T(M4#II8yxoY(I6Dge5qrzDY0IySp4%!UbN&@;=z4!&=e>cPS{1*Z6g=E$?5e)b
z$$T@$TJnlzZt)!HrbU|8otV~UU#~dqm=Rv5PEnpQ$uuST@jnmsWFph<(o-?^5c}v0
zp9#I%`mzrU_@phlx4Cea^wrbfsWO$|sDGsP3NrIzslM26_K^A%_>AN^K8VHI5TVh>
z?)-qE=^z;Wo;>}za1l;~9HgE7;9+{q#woK0i`GPtrl2x0_%#}bH+^HfePN$|^y}Hu
zbd2yIvqyb8SRbYOC@mKx<#hMiz}Cj~Q8T38$nNEmjL#xib2tsRQX*y0CZCzwN-GZ)
zQru9(T>nta0xj_te;jqA`__^?vHUm#p40W~C|Wq>2wmIx9w4J530_Kc*J%f$=ryvH
zcFuX`M{u|plGf-3n5Oom6>-131M_LicJQ%}RI4qYBQF+;V$kj&V{>1QJytCb=Q*pc
zvh_{}`IXIC?3c75EZ<=P~;lTfma?1tJjy>w$YVF6UVqx?#PPF#{mkGY
zvs1Ewq$YPK%Vlp1{rayIQ8v$~faynIW%PWse?EtrWY(S8A6AiH_zcRR562cVBdK$x
zSR&G&q*HY)gY+N?$Ihy4kXzAm8lv?kwoPmpu~dw_*(l>(Sjj+}M{$i^JfPb)5vk>Ei1`<|P#6l@Zf1H4
z!Hov1BF@vwkqm_yZ3zOK1J#vy>~6==huV!S1gX~%kltrBhXw~MnNqoTel3fUWoT0gl4UZe^6m4ZF8=VB0M9VJ`gB{+n2*DAb`3-Mb
zNM~b+03FRVYi!nPW_*nVV_=!oG#++H#%G|J9ztdlGK(Vbg;`#Tzq`3Nl8kwN;^;I`U$@#>}^Qk
zxcXV>OxXKjn9-JRMcH>Z+S;BLn=nHK)aj*$_(
z5!-~RE?*`4zdUCeM~1??!~G-Red^IcR#`c&v
z#n&uMh%|PHQbVHt)8po^CBxFxh5&(|Vk<^R+Gi*Mh5}a?%UNO8#(D|-Pgi*X5JYb#
z=aAot)6+t8LOy$?E}cN9?hCUQ
zH_n*(F(;(SJ4{i|4NZ#%*ArkLr{AGhN^Q*rytXht6p_3JY`_;L!|^nnKYyv#XqNi5
z7%O*~dBlV_T5Y6oq1)0CvUc1QY);J#+qH}oa?-tt4`W{tJ!)D&%&IIP)%s`V)MPPD
zdyLba*)jQ)A~%(wg)9s2&W|nPWtEY|R?{8!#j9zajDt_~?>O4O9QYLenPA$pxW`)G
zdA(?~(iF7moD~NPK=JIo5eqsyStzzEaRgM{s8$f}3;V_%m
zlTG|`Deo|+ZjmYx9v=H3b4xG>^@J?w&6c@`gq;kF4AbLE}9k_
z+G8v&4PiUJ+-29Uki9XB!82+JynZY~<8nn9j5}WU-k6;0jo=j5=w9-^-ww2_;#XY%
zW*&Nw$lDsQZ~iCG^f=}DU*gsI0xt%2OJEKs0lo;pqiN}98F5f!i#}>SWjmfr3bS9aI1RxQBbNApn{&Pa|U9rjBw?+D94jGYo
z5}?&w^bRzLMC41yX+da5L@b)Iu=iefuU7fK1trK($aD9fE01nDsK(>?h?!?F_Mt#o
z?7+gPDmOE@cZ-b^X$&U;T>qmS8`U7IhznY@B`bBBQ#ComqVPnr(xs$95W_$r9A{Kz
zHkeqChHKnP<#qY(^nR*7+oXt5%-Z^oqqR}h+f_2R;Vt>^7Kx=5F&dbM;s0(CIpXH#
zWNK#9|GvYb`%diok;`r;+vjEUnQGz719Y!D>==v4`Slu9i*BcJ4&%#oy+_E;rT!jS
zVAvb{Vc1zKS4==sNt|SPnW%cC5vF#uKNLd)+cJ~r|;PF
zLS?nH=Pepc!@5B+S~`!V4?fVge|Lf(&bPK|XtvY@ZK4=pEe%w{ffu8i_vLnJ8YGi~
zEU088kUf5(jYU?sD!a-2yxT~n!Z@v_QxDnSc~%@m-N)-d*z~<`v6V{9UkcxAHxB(i
zC9cD#^?=XSYv|g%gY1>+RTUjJBsRIWf@P~8uUcGe;gIL`08p7qVFj$k6AqgONskIo
z=+*iLIMia#LOlO)%mTgiNaM`DzkkXoC>NWfzJ50|6nMVt>ws?utF@;zOi(y|S?+wv
zV4WTO`RJgV8WUY8>~a?_u@vq+LAK4&EYqh0aj(C3g|PX(G~SP_51_+|TG2gw5!C^W
z?AJZXN6xx3IU~*a6aSP4drys)TYQ5I^>3e@#TW(_ZK?%>*~D84=R|Cxz9x-;oX7k>
zzGT!6Hdc^n^na2*IB~mwTKCiPT_iT6;1NMjvI0Wl=Oof)8XI?UyovBdhS%-SrUSd>
z*kjvY3AF!NXs>mZOe-AJGYikkQkqkJT3N2POFSnTCJJANNCwS
zLnI|UG!ybX1cFljJ;nBqV38bb1b)XS^jtwK;UGDRrevCK8~~jpBP7-5L`%tHlzHwf
zy!UKpS1sCWUeY0{QdqOgE~Mr$xw*Qib;;`JeaYFR?c|&2n!n7OU-QH;VDmEUI9B
z3`8|<88{4A@42XLzGvvl)$jWQrNVn8YUH%yplQejF=Q*-nZM7na6&b{FP3Uj@4>89
zcKSi|anIdWcnn(Ht=2e-=KR-bq9@m<+;
z>C617Sre`c-dHz_Bb^$VQdnhI1vq_S>s_x@r~FAXCEwKtBEUMsh%Y&TFX
zHrtz$kcG+f-<9r(8RKyS;1T=
zcH2cQCzpnv;a}t`S#rN3AWC~~IQ<|XtPc*UKR81SHkj5|$Nyn7uV$+*j>M@&0C%XX@(aYD
z;D&mTYfhVUs*
zME6H~Tea#N{2Ia1LW!p9ak=TkZmV|1D-0`QR>ug62TnWcsroI;>tx_X`6`x}{!l~>
z@OTTw;W5zrR~ztopGd?1IjqqZNXy^o9q)!VFoJ?!ONpB8HqtdUT`ki}#t9LtA*mCS
z>FWr1szl=ubh}zhIzuJ^AIfYzBk)m3e?Fa@oE0O@&6q$UT$b&4d7yFn-emnpR76y}
zTWWnO>`FGT;krUb%wZm4Toj)a>EAK2XROvOb%_9GsZ1jbZ|ZA*dHBl4NXE&=5a|^1
z+Y>%tw)=d~y<9jjI}>%U?d-fGBVo97DC9AchOoY7$h{hACZqgChu(19T&8T^{#O6#
z{t8YsW?#itaa-!(XI+#^R1bT(y`_9&qfL9cDC%i*(yGaI
z!UIs0+L~ErV7DEH%-PdMTrC6iRdAM%l#^ig`Wg|n#WEj9&xX20;hE-E)?bU*`4Htj
zRJ~-Er7{X1K7RVrMfiX{S{q;)Z2i@9#+5Z*ukBx%%vfB5AK9%!w7(0DT;D@lhsGn%
zI|$zea{hf;FO#{u_gru$=io+-+~Px;_&5g(PxqmTA<|@r`gG2=0>|Bq75!r(D^j42
zuBsEmR|4zpa?Q1XR1bKWV>tVPShVKHY6;X`{o|K{k*)3Udd=XGNo?KFxgk*hUtNnO
zOpA6v
zy`K6rf*22XsPk*(Mz;ZD#J}j`PlldU0akJvhl+zcYK_*YBhK1l@463jppzL3bEj&{0d^0eFCcS8tHGV9Qme`i*NH_ry8K$w?t?^FCZRYKRgDL!5UYuv-5ZV(QaZU^}Kr{c9Zo*qm%q^FhRx>Pu*Us
zUmOXK``rak5P}&JL__2){JXxaio}xj3$JDuDNX2tKPk;{WTecY$z{3>su7QVE)O?P
zm#_tcImmzDq%|tx*=FLFEpNG?w3ZimC))vrF!I!>HA3HyxunZi_(n`*3wGRWBy@T^
z8LbCTM~l;pY*togPFR|30JM
z%Nu;|H3`f5F}-R;mv-(P*1yXUoL~6>=Ly?DTq?7CIjql8lp(*+Iu*6v_I%khGc&_`
zs-!UL7~iw(nyE7FbVs|cT=d#lyrP8@jb~KP4chEBaajbxPW^X+^cF9_l~kRb89uz|
zr(M0ix*E$jKvj$ImaB14s2&wqeG|A|@OAm%MfO
zHaX7RpIhm{Q4-KUv0Qf8SZj#j0%3dpR017fH(iNXfzy2@69Lom)
literal 0
HcmV?d00001
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..011c681
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,258 @@
+# -*- coding: utf-8 -*-
+#
+# Isso documentation build configuration file, created by
+# sphinx-quickstart on Thu Nov 21 11:28:01 2013.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+from os.path import join, dirname
+sys.path.insert(0, join(dirname(__file__), "_isso/"))
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinx.ext.todo',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'docs/index'
+
+# General information about the project.
+project = u'Isso'
+copyright = u'2013, Martin Zimmermann'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.5'
+# The full version, including alpha/beta/rc tags.
+release = '0.5.1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'trac'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = '_isso'
+html_translator_class = "html5.Isso"
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+html_theme_path = ["."]
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# " v documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+html_additional_pages = {"index": "index.html"}
+
+# If false, no module index is generated.
+html_domain_indices = False
+
+# If false, no index is generated.
+html_use_index = False
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Issodoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ ('index', 'Isso.tex', u'Isso Documentation',
+ u'Martin Zimmermann', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'isso', u'Isso Documentation',
+ [u'Martin Zimmermann'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'Isso', u'Isso Documentation',
+ u'Martin Zimmermann', 'Isso', 'a commenting server similar to Disqus',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
diff --git a/docs/contribute.rst b/docs/contribute.rst
new file mode 100644
index 0000000..709ae46
--- /dev/null
+++ b/docs/contribute.rst
@@ -0,0 +1,4 @@
+Contribute
+==========
+
+To be writ.. wait.
diff --git a/docs/docs/configuration/client.rst b/docs/docs/configuration/client.rst
new file mode 100644
index 0000000..4db5151
--- /dev/null
+++ b/docs/docs/configuration/client.rst
@@ -0,0 +1,52 @@
+Client Configuration
+====================
+
+You can configure the client (the JS part) with custom data attributes,
+preferably in the script tag which embeds the JS:
+
+.. code-block:: html
+
+
+
+Furthermore you can override the automatic title detection inside
+the embed tag, e.g.:
+
+.. code-block:: html
+
+
+
+data-isso
+---------
+
+Isso usually detects the REST API automatically, but when you serve the JS
+script on a different location, this may fail. Use `data-isso` to
+override the API location:
+
+.. code-block:: html
+
+
+
+data-isso-css
+-------------
+
+Set to `false` prevents Isso from automatically appending the stylesheet.
+Defaults to `true`.
+
+.. code-block:: html
+
+
+
+data-isso-lang
+--------------
+
+Override useragent's preferred language. Currently available: german (de),
+english (en) and french (fr).
+
+data-isso-reply-to-self
+-----------------------
+
+Set to `true` when spam guard is configured with `reply-to-self = true`.
diff --git a/docs/CONFIGURATION.rst b/docs/docs/configuration/server.rst
similarity index 71%
rename from docs/CONFIGURATION.rst
rename to docs/docs/configuration/server.rst
index 3e3cd6f..0e964f8 100644
--- a/docs/CONFIGURATION.rst
+++ b/docs/docs/configuration/server.rst
@@ -1,8 +1,8 @@
-Isso Configuration
-==================
+Server Configuration
+====================
-The Isso configuration file is an `INI-style`__ textfile. Below is an example for
-a basic Isso configuration. Each section has its own documentation.
+The Isso configuration file is an `INI-style`__ textfile. It reads integers,
+booleans and strings. Below is a basic example:
.. code-block:: ini
@@ -12,8 +12,8 @@ a basic Isso configuration. Each section has its own documentation.
[server]
port = 1234
-You can point Isso to your configuration file either witg a command-line parameter
-or using an environment variable:
+To use your configuration file with Isso, append ``-c /path/to/cfg`` to the
+executable or run Isso with an environment variable:
.. code-block:: sh
@@ -22,6 +22,11 @@ or using an environment variable:
__ https://en.wikipedia.org/wiki/INI_file
+Sections covered in this document:
+
+.. contents::
+ :local:
+
General
-------
@@ -35,7 +40,7 @@ session key and hostname. Here are the default values for this section:
dbpath = /tmp/isso.db
host = http://localhost:8080/
max-age = 15m
- session-key = ... # python: binascii.b2a_hex(os.urandom(24))
+ session-key = ... ; python: binascii.b2a_hex(os.urandom(24))
notify =
dbpath
@@ -131,6 +136,7 @@ profile
show 10 most time consuming function in Isso after each request. Do
not use in production.
+.. _configure-smtp:
SMTP
----
@@ -208,66 +214,8 @@ reply-to-self
Do not forget to configure the client.
-Multiple Sites
---------------
-
-Isso is designed to serve comments for a single website and therefore stores
-comments for a relative URL to support HTTP, HTTPS and even domain transfers
-without manual intervention. But you can chain Isso to support multiple
-websites on different domains.
-
-The following example uses `gunicorn `_ as WSGI server (
-you can use uWSGI as well). Let's say you maintain two websites, like
-foo.example and other.foo:
-
-.. code-block:: bash
-
- $ cat /etc/isso.d/foo.example.cfg
- [general]
- host = http://foo.example/
- dbpath = /var/lib/isso/foo.example.db
-
- $ cat /etc/isso.d/other.foo.cfg
- [general]
- host = http://other.foo/
- dbpath = /var/lib/isso/other.foo.db
-
-Then you run Isso using gunicorn:
-
-.. code-block:: bash
-
- $ export ISSO_SETTINGS="/etc/isso.d/foo.example.cfg;/etc/isso.d/other.foo.cfg"
- $ gunicorn isso.dispatch -b localhost:8080
-
-In your webserver configuration, proxy Isso as usual:
-
-.. code-block:: nginx
-
- server {
- listen [::]:80;
- server_name comments.example;
-
- location / {
- proxy_pass http://localhost:8080;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Real-IP $remote_addr;
- }
- }
-
-To verify the setup, run:
-
-.. code-block:: bash
-
- $ curl -vH "Origin: http://foo.example" http://comments.example/
- ...
- $ curl -vH "Origin: http://other.foo" http://comments.example/
- ...
-
-In case of a 418 (I'm a teapot), the setup is *not* correctly configured.
-
-
Appendum
----------
+--------
.. _appendum-timedelta:
diff --git a/docs/docs/configuration/setup.rst b/docs/docs/configuration/setup.rst
new file mode 100644
index 0000000..adc1584
--- /dev/null
+++ b/docs/docs/configuration/setup.rst
@@ -0,0 +1,90 @@
+Setup
+=====
+
+Sub-URI
+-------
+
+You can run Isso on the same domain as your website, which circumvents issues
+originating from CORS_. Also, privacy-protecting browser addons such as
+`Request Policy`_ wont block comments.
+
+.. code-block:: nginx
+
+ server {
+ listen [::]:80;
+ listen [::]:443 ssl;
+ server_name example.tld;
+ root /var/www/example.tld;
+
+ location /isso {
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Script-Name /isso;
+ proxy_pass http://localhost:8080;
+ }
+ }
+
+Now, the website integration is just as described in :doc:`../quickstart` but
+with a different location.
+
+.. _CORS: https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS
+.. _Request Policy: https://www.requestpolicy.com/
+
+
+.. _configure-multiple-sites:
+
+Multiple Sites
+--------------
+
+Isso is designed to serve comments for a single website and therefore stores
+comments for a relative URL to support HTTP, HTTPS and even domain transfers
+without manual intervention. But you can chain Isso to support multiple
+websites on different domains.
+
+The following example uses `gunicorn `_ as WSGI server (
+you can use uWSGI as well). Let's say you maintain two websites, like
+foo.example and other.foo:
+
+.. code-block:: sh
+
+ $ cat /etc/isso.d/foo.example.cfg
+ [general]
+ host = http://foo.example/
+ dbpath = /var/lib/isso/foo.example.db
+
+ $ cat /etc/isso.d/other.foo.cfg
+ [general]
+ host = http://other.foo/
+ dbpath = /var/lib/isso/other.foo.db
+
+Then you run Isso using gunicorn:
+
+.. code-block:: sh
+
+ $ export ISSO_SETTINGS="/etc/isso.d/foo.example.cfg;/etc/isso.d/other.foo.cfg"
+ $ gunicorn isso.dispatch -b localhost:8080
+
+In your webserver configuration, proxy Isso as usual:
+
+.. code-block:: nginx
+
+ server {
+ listen [::]:80;
+ server_name comments.example;
+
+ location / {
+ proxy_pass http://localhost:8080;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Real-IP $remote_addr;
+ }
+ }
+
+To verify the setup, run:
+
+.. code-block:: sh
+
+ $ curl -vH "Origin: http://foo.example" http://comments.example/
+ ...
+ $ curl -vH "Origin: http://other.foo" http://comments.example/
+ ...
+
+In case of a 418 (I'm a teapot), the setup is *not* correctly configured.
diff --git a/docs/docs/extras/api.rst b/docs/docs/extras/api.rst
new file mode 100644
index 0000000..281f836
--- /dev/null
+++ b/docs/docs/extras/api.rst
@@ -0,0 +1,98 @@
+Isso API
+========
+
+The Isso API uses HTTP and JSON as primary communication protocol.
+
+JSON format
+-----------
+
+When querying the API you either get an error, an object or list of
+objects representing the comment. Here's a example JSON returned from
+Isso:
+
+.. code-block:: js
+
+ {
+ "text": "Hello, World!",
+ "author": "Bernd",
+ "website": null,
+ "votes": 0,
+ "mode": 1,
+ "id": 1,
+ "parent": null,
+ "hash": "68b329da9893e34099c7d8ad5cb9c940",
+ "created": 1379001637.50,
+ "modified": null
+ }
+
+text : required, comment as HTML
+
+author : author's name, may be ``null``
+
+website : author's website, may be ``null``
+
+votes : sum of up- and downvotes, defaults to zero.
+
+mode : \* 1, accepted comment \* 2, comment in moderation queue \* 4,
+comment deleted, but is referenced
+
+id : unique comment number per thread
+
+parent : answer to a parent id, may be ``null``
+
+hash : user identification, used to generate identicons
+
+created : time in seconds sinde epoch
+
+modified : last modification time in seconds, may be ``null``
+
+List comments
+-------------
+
+List all visible comments for a thread. Does not include deleted and
+comments currently in moderation queue.
+
+ GET /?uri=path
+
+You must encode ``path``, e.g. to retrieve comments for
+``/hello-world/``:
+
+::
+
+ GET /?uri=%2Fhello-world%2F
+
+To disable automatic Markdown-to-HTML conversion, pass ``plain=1`` to
+the query URL:
+
+::
+
+ GET /?uri=...&plain=1
+
+As response, you either get 200, 400, or 404, which are pretty
+self-explanatory.
+
+::
+
+ GET /
+ 400 BAD REQUEST
+
+ GET /?uri=%2Fhello-world%2F
+ 404 NOT FOUND
+
+ GET /?uri=%2Fcomment-me%2F
+ [{comment 1}, {comment 2}, ...]
+
+Create comments
+---------------
+
+...
+
+Delete comments
+---------------
+
+...
+
+Up- and downvote comments
+-------------------------
+
+...
diff --git a/docs/docs/extras/multi-site.rst b/docs/docs/extras/multi-site.rst
new file mode 100644
index 0000000..be51242
--- /dev/null
+++ b/docs/docs/extras/multi-site.rst
@@ -0,0 +1,56 @@
+Multi Site Configuration
+========================
+
+Isso is designed to serve comments for a single website and therefore stores
+comments for a relative URL to support HTTP, HTTPS and even domain transfers
+without manual intervention. But you can chain Isso to support multiple
+websites on different domains.
+
+The following example uses `gunicorn `_ as WSGI server (
+you can use uWSGI as well). Let's say you maintain two websites, like
+foo.example and other.foo:
+
+.. code-block:: bash
+
+ $ cat /etc/isso.d/foo.example.cfg
+ [general]
+ host = http://foo.example/
+ dbpath = /var/lib/isso/foo.example.db
+
+ $ cat /etc/isso.d/other.foo.cfg
+ [general]
+ host = http://other.foo/
+ dbpath = /var/lib/isso/other.foo.db
+
+Then you run Isso using gunicorn:
+
+.. code-block:: bash
+
+ $ export ISSO_SETTINGS="/etc/isso.d/foo.example.cfg;/etc/isso.d/other.foo.cfg"
+ $ gunicorn isso.dispatch -b localhost:8080
+
+In your webserver configuration, proxy Isso as usual:
+
+.. code-block:: nginx
+
+ server {
+ listen [::]:80;
+ server_name comments.example;
+
+ location / {
+ proxy_pass http://localhost:8080;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Real-IP $remote_addr;
+ }
+ }
+
+To verify the setup, run:
+
+.. code-block:: bash
+
+ $ curl -vH "Origin: http://foo.example" http://comments.example/
+ ...
+ $ curl -vH "Origin: http://other.foo" http://comments.example/
+ ...
+
+In case of a 418 (I'm a teapot), the setup is *not* correctly configured.
diff --git a/docs/docs/extras/uwsgi.rst b/docs/docs/extras/uwsgi.rst
new file mode 100644
index 0000000..018a86d
--- /dev/null
+++ b/docs/docs/extras/uwsgi.rst
@@ -0,0 +1,57 @@
+uWSGI
+=====
+
+In short: `uWSGI `_ is awesome. Isso
+has builtin support for it (and simple fallback if uWSGI is not
+available). Use uWSGI if you think that the builtin WSGI server is a bad
+choice or slow (hint: it's both).
+
+With uWSGI, you have roughly 100% performance improvements for just
+using it. Instead of one thread per request, you can use multiple
+processes, hence it is more "web scale". Other side effects: spooling,
+fast inter-process caching.
+
+Installation
+------------
+
+You need uWSGI 1.9 or higher, fortunately you can install it with
+Python:
+
+.. code-block:: sh
+
+ ~> apt-get install build-essential python-dev
+ ~> pip install uwsgi
+
+Configuration
+-------------
+
+For convenience, I recommend a INI-style configuration (you can also
+supply everything as command-line arguments):
+
+.. code-block:: ini
+
+ [uwsgi]
+ http = :8080
+ master = true
+ processes = 4
+ cache2 = name=hash,items=1024,blocksize=32
+ spooler = %d/mail
+ module = isso
+ virtualenv = %d
+ env = ISSO_SETTINGS=%d/sample.cfg
+
+You shoud adjust ``processes`` to your CPU count. Then, save this file
+to a directory if choice. Next to this file, create an empty directory
+called ``mail``:
+
+.. code-block:: sh
+
+ ~> mkdir mail/
+ ~> ls
+ uwsgi.ini mail/
+
+Now start Isso:
+
+.. code-block:: sh
+
+ ~> uwsgi /path/to/uwsgi.ini
diff --git a/docs/docs/index.rst b/docs/docs/index.rst
new file mode 100644
index 0000000..a740aa6
--- /dev/null
+++ b/docs/docs/index.rst
@@ -0,0 +1,47 @@
+Overview
+========
+
+Welcome to the Isso's documentation. This documentation will help you get
+started fast. If you get any problems when using Isso, you can find the answer
+in troubleshooting or you can ask me on IRC or GitHub.
+
+What's Isso?
+------------
+
+Isso is a lightweight commenting server similar to Disqus. It allows anonymous
+comments, maintains identity and is simple to administrate. It uses JavaScript
+and cross-origin ressource sharing for easy integration into static websites.
+
+No, I meant "Isso"
+------------------
+
+Isso is an informal, german abbreviation for "Ich schrei sonst!", which can
+roughly be translated to "I'm yelling otherwise". It usually ends the
+discussion without any further arguments.
+
+In germany, Isso `is also pokémon N° 360`__.
+
+.. __: http://bulbapedia.bulbagarden.net/wiki/Wynaut_(Pok%C3%A9mon)
+
+What's wrong with Disqus?
+-------------------------
+
+No anonymous comments (IP address, email and name recorded), hosted in the USA,
+third-party. Just like IntenseDebate, livefrye etc. When you embed Disqus, they
+can do anything with your readers (and probably mine Bitcoins, see the loading
+times).
+
+Isso is not the first open-source commenting server:
+
+ * `talkatv `_, written in Python.
+ Unfortunately, talkatv's (read "talkative") development stalled. Neither
+ anonymous nor threaded comments.
+
+ * `Juvia `_, written in Ruby (on Rails).
+ No threaded comments, nice administration webinterface, but... yeah... Ruby.
+
+ * `Tildehash.com `_,
+ written in PHP. Did I forgot something?
+
+ * `Unobtrusive, self-hosted comments `_,
+ discussion on StackOverflow.
diff --git a/docs/docs/install.rst b/docs/docs/install.rst
new file mode 100644
index 0000000..6a86d30
--- /dev/null
+++ b/docs/docs/install.rst
@@ -0,0 +1,24 @@
+Installation
+------------
+
+Requirements:
+
+- Python 2.6, 2.7 or 3.3
+- a working C compiler
+
+Install Isso with PIP_:
+
+.. code-block:: sh
+
+ ~> pip install isso
+
+.. _PIP: http://www.pip-installer.org/en/latest/
+
+Init scripts:
+
+- SystemD: `isso.service
+ `_
+- SysVinit: `isso.init
+ `_
+- OpenBSD: `GH:Gist `_
+
diff --git a/docs/docs/quickstart.rst b/docs/docs/quickstart.rst
new file mode 100644
index 0000000..4defe85
--- /dev/null
+++ b/docs/docs/quickstart.rst
@@ -0,0 +1,187 @@
+Quickstart
+==========
+
+Assuming you have successfully :doc:`installed ` Isso, here's
+a quickstart quide that covers common setups.
+
+
+Configuration
+-------------
+
+You must provide a custom configuration. Most default parameters are useful for
+development, not persistence. Two most important options are `dbpath` to set
+the location of your database and `host` which is your website:
+
+.. code-block:: ini
+
+ [general]
+ ; database location, check permissions
+ dbpath = /var/lib/isso/comments.db
+ ; your website or blog (not the location of Isso!)
+ host = http://example.tld/
+ ; you can add multiple hosts for local development
+ ; or SSL connections. There is no wildcard to allow
+ ; any domain.
+ host =
+ http://localhost:1234/
+ http://example.tld/
+ https://example.tld/
+
+Note, that multiple, *different* websites are **not** supported in a single
+configuration. To serve comments for diffent websites, refer to
+:ref:`Multiple Sites `.
+
+You moderate Isso through signed URLs sent by email or logged. By default,
+comments are accepted and immediately shown to other users. To enable
+moderation queue, add:
+
+.. code-block:: ini
+
+ [moderation]
+ enabled = true
+
+To moderate comments, either use the activation or deletion URL in the logs or
+:ref:`use SMTP ` to get notified on new comments including the
+URLs for activation and deletion:
+
+.. code-block:: ini
+
+ [general]
+ notify = smtp
+ [smtp]
+ ; SMTP settings
+
+
+Migration
+---------
+
+You can migrate your existing comments from Disqus_. Log into Disqus, go to
+your website, click on *Discussions* and select the *Export* tab. You'll
+receive an email with your comments. Unfortunately, Disqus does not export
+up- and downvotes.
+
+To import existing comments, run Isso with your new configuration file:
+
+.. code-block:: sh
+
+ ~> isso -c /path/to/isso.cfg import user-2013-09-02T11_39_22.971478-all.xml
+ [100%] 53 threads, 192 comments
+
+.. _Disqus:
+
+
+Running Isso
+------------
+
+To run Isso, simply execute:
+
+.. code-block:: sh
+
+ $ isso -c /path/to/isso.cfg run
+ 2013-11-25 15:31:34,773 INFO: connected to HTTP server
+
+Next, we configure Nginx_ to proxy Isso. Do not run Isso on a public interface!
+A popular but often error-prone (because of CORS_) setup to host Isso uses a
+dedicated domain such as ``comments.example.tld``; see
+:doc:`configuration/setup` for alternate ways.
+
+Assuming both, your website and Isso are on the same server, the nginx
+configuration looks like this:
+
+.. code-block:: nginx
+
+ server {
+ listen [::]:80 default ipv6only=off;
+ server_name example.tld;
+ root ...;
+ }
+
+ server {
+ listen [::]:80;
+ server_name comments.example.tld;
+
+ location / {
+ proxy_pass http://localhost:8080;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header Host $host;
+ }
+ }
+
+Now, you embed Isso to your website:
+
+.. code-block:: html
+
+
+
+
+
+Note, that `data-isso` is optional, but when a website includes a script using
+``async`` it is no longer possible to determine the script's external URL.
+
+That's it. When you open your website, you should see a commenting form. Leave
+a comment to see if the setup works. If not, see :doc:`troubleshooting`.
+
+.. _Nginx: http://nginx.org/
+.. _CORS: https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS
+
+
+Deployment
+----------
+
+Isso ships with a built-in web server, which is useful for the initial setup.
+But it is not recommended to use the built-in web server for production. A few
+WSGI servers are supported out-of-the-box:
+
+ * gevent_, coroutine-based network library
+ * uWSGI_, full-featured uWSGI server
+ * gunicorn_, Python WSGI HTTP Server for UNIX
+
+.. _gevent: http://www.gevent.org/
+.. _uWSGI: http://uwsgi-docs.readthedocs.org/en/latest/
+.. _gunicorn: http://gunicorn.org/
+
+gevent
+^^^^^^
+
+Probably the easiest deployment method. Install with PIP (requires libevent):
+
+.. code-block:: sh
+
+ $ pip install gevent
+
+Then, just use the ``isso`` executable as usual. Gevent monkey-patches Python's
+standard library to work with greenlets.
+
+To execute Isso, just use the commandline interface:
+
+.. code-block:: sh
+
+ $ isso -c my.cfg run
+
+Unfortunately, gevent 0.13.2 does not support UNIX domain sockets (see `#295
+`_ and `#299
+`_ for details).
+
+uWSGI
+^^^^^
+
+The author's favourite WSGI server. Due the complexity of uWSGI, there is a
+:doc:`separate document ` on how to setup uWSGI for use
+with Isso.
+
+gunicorn
+^^^^^^^^
+
+Install gunicorn_ via PIP:
+
+.. code-block:: sh
+
+ $ pip install gunicorn
+
+To execute Isso, use a command similar to:
+
+.. code-block:: sh
+
+ $ export ISSO_SETTINGS="/path/to/isso.cfg"
+ $ gunicorn -b localhost:8080 -w 4 --preload isso
diff --git a/docs/docs/troubleshooting.rst b/docs/docs/troubleshooting.rst
new file mode 100644
index 0000000..56d1a8a
--- /dev/null
+++ b/docs/docs/troubleshooting.rst
@@ -0,0 +1,4 @@
+Troubleshooting
+===============
+
+To be written.
diff --git a/docs/docs/usage.rst b/docs/docs/usage.rst
new file mode 100644
index 0000000..d4b9a15
--- /dev/null
+++ b/docs/docs/usage.rst
@@ -0,0 +1,2 @@
+Usage
+=====
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..7086a69
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,60 @@
+{%- extends "layout.html" %}
+{% set title = "Isso – a commenting server similar to Disqus" %}
+{% block extrahead %}
+
+{% endblock %}
+{% block header %}
+
+
+
+
+
+ by Randall Munroe, CC BY-NC 2.5
+
+
+
+ Create, Edit and Remove Comments
+ Commenters can edit or delete their own comments (within
+ 15 minutes by default).
+ Comments in moderation queue are not publicly visible until
+ activation.
+
+
+ SQLite backend
+ Because.
+
+
+ Disqus Import
+ You can migrate your Disqus comments without any hassle.
+
+
+ client-side JavaScript
+ Embed a single JS file, 54kb (18kb gzipped) and you are
+ done.
+ Supports Firefox, Safari, Chrome and IE10.
+
+
+
+{% endblock %}
+{% block body %}
+
+
Links
+
+
Help
+
+ Join
+ #isso
on Freenode
+ or open an issue on GitHub .
+
+ {% include "searchbox.html" %}
+
+
+
+
Demo
+
+
+{% endblock %}
diff --git a/docs/news.rst b/docs/news.rst
new file mode 100644
index 0000000..5ae8f8b
--- /dev/null
+++ b/docs/news.rst
@@ -0,0 +1,4 @@
+News
+====
+
+To be written.
diff --git a/docs/uWSGI.md b/docs/uWSGI.md
deleted file mode 100644
index 8321769..0000000
--- a/docs/uWSGI.md
+++ /dev/null
@@ -1,55 +0,0 @@
-uWSGI
-=====
-
-In short: [uWSGI](http://uwsgi-docs.readthedocs.org/) is awesome. Isso has
-builtin support for it (and simple fallback if uWSGI is not available). Use
-uWSGI if you think that the builtin WSGI server is a bad choice or slow (hint:
-it's both).
-
-With uWSGI, you have roughly 100% performance improvements for just using it.
-Instead of one thread per request, you can use multiple processes, hence it
-is more "web scale". Other side effects: spooling, fast inter-process caching.
-
-
-Installation
-------------
-
-You need uWSGI 1.9 or higher, fortunately you can install it with Python:
-
-```sh
-~> apt-get install build-essential python-dev
-~> pip install uwsgi
-```
-
-Configuration
--------------
-
-For convenience, I recommend a INI-style configuration (you can also supply everything
-as command-line arguments):
-
-```ini
-[uwsgi]
-http = :8080
-master = true
-processes = 4
-cache2 = name=hash,items=1024,blocksize=32
-spooler = %d/mail
-module = isso
-virtualenv = %d
-env = ISSO_SETTINGS=%d/sample.cfg
-```
-
-You shoud adjust `processes` to your CPU count. Then, save this file to a directory
-if choice. Next to this file, create an empty directory called `mail`:
-
-```sh
-~> mkdir mail/
-~> ls
-uwsgi.ini mail/
-```
-
-Now start Isso:
-
-```sh
-~> uwsgi /path/to/uwsgi.ini
-```