use sphinx for documentation

pull/41/head
Martin Zimmermann 11 years ago
parent c9c0df229a
commit b79ac583e8

@ -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

@ -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
<html>
<body>
<h1>Website Title</h1>
<article>
<header>
<h1>Post Title</h1>
<section id="isso-thread">
...
```
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
<section data-title="Post Title" id="isso-thread">
```
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
<script data-isso="/isso" src="/path/to/embed.min.js"></script>
```
* data-isso-css
Set to `false` prevents Isso from automatically appending the stylesheet.
Defaults to `true`.
```html
<script src="..." data-isso-css="false"></script>
```
* 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`:

@ -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
...

File diff suppressed because it is too large Load Diff

@ -0,0 +1,123 @@
<!DOCTYPE html>
{%- set url_root = pathto('', 1) %}
{# XXX necessary? #}
{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
{%- macro script() %}
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '{{ url_root }}',
VERSION: '{{ release|e }}',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
HAS_SOURCE: {{ has_source|lower }}
};
</script>
{%- for scriptfile in script_files %}
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
{%- endfor %}
{%- endmacro %}
{%- macro css() %}
<!--link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" /-->
<link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
<link rel="stylesheet" href="{{ pathto('_static/test.css', 1) }}"/>
{%- for cssfile in css_files %}
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
{%- endfor %}
{%- endmacro %}
<html>
<head>
<meta charset="utf-8">
{{ metatags }}
{%- block htmltitle %}
<title>{{ title|striptags|e }}</title>
{%- endblock %}
{{ css() }}
{%- if not embedded %}
{{ script() }}
{%- if use_opensearch %}
<link rel="search" type="application/opensearchdescription+xml"
title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}"
href="{{ pathto('_static/opensearch.xml', 1) }}"/>
{%- endif %}
{%- if favicon %}
<link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
{%- endif %}
{%- endif %}
{%- block linktags %}
{%- if hasdoc('about') %}
<link rel="author" title="{{ _('About these documents') }}" href="{{ pathto('about') }}" />
{%- endif %}
{%- if hasdoc('genindex') %}
<link rel="index" title="{{ _('Index') }}" href="{{ pathto('genindex') }}" />
{%- endif %}
{%- if hasdoc('search') %}
<link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}" />
{%- endif %}
{%- if hasdoc('copyright') %}
<link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}" />
{%- endif %}
<link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}" />
{%- if parents %}
<link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}" />
{%- endif %}
{%- if next %}
<link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}" />
{%- endif %}
{%- if prev %}
<link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}" />
{%- endif %}
{%- endblock %}
{%- block extrahead %} {% endblock %}
</head>
<body>
<div class="wrapper">
<div class="header">
<header>
<img class="logo" src="{{ pathto('_static/isso.svg', 1) }}" alt="Wynaut by @veekun"/>
<div class="title">
<a href="{{ pathto(".") }}">
<h1>Isso</h1>
<h2>a commenting server similar to Disqus</h2>
</a>
</div>
</header>
<nav>
<ul>
<li><a href="{{ pathto('news') }}">News</a></li>
<li><a href="{{ pathto('contribute') }}">Contribute</a></li>
<li><a href="{{ pathto('docs') }}">Documentation</a></li>
</ul>
</nav>
</div>
<div class="outer">
{% block header %} {% endblock %}
</div>
<main>
{% block body %} {% endblock %}
</main>
<div class="push"></div>
</div>
<div class="outer footer">
{%- block footer %}
<footer>
&copy; Copyright {{ copyright }}.
{%- if last_updated %}
Last updated on {{ last_updated }}.
{%- endif %}
Made with <a href="http://sphinx-doc.org/">Sphinx</a>.
</footer>
</div>
{%- endblock %}
</body>
</html>

@ -0,0 +1,16 @@
{%- extends "layout.html" %}
{% block header %}
<header>
<h1>{{- title -}}</h1>
</header>
{% endblock %}
{% block body %}
<div class="sidebar">
{% if pagename.startswith("docs/") %}
{% include "sidebar-docs.html" %}
{% endif %}
</div>
<div class="docs">
{{ body }}
</div>
{% endblock %}

@ -0,0 +1,54 @@
{% extends "layout.html" %}
{% block header %}
<header>
<h1>Search</h1>
</header>
{% endblock %}
{% set title = _('Search') %}
{% set script_files = script_files + ['_static/searchtools.js'] %}
{% block extrahead %}
<script type="text/javascript">
jQuery(function() { Search.loadIndex("{{ pathto('searchindex.js', 1) }}"); });
</script>
{# this is used when loading the search index using $.ajax fails,
such as on Chrome for documents on localhost #}
<script type="text/javascript" id="searchindexloader"></script>
{{ super() }}
{% endblock %}
{% block body %}
<div id="fallback" class="admonition warning">
<script type="text/javascript">$('#fallback').hide();</script>
<p>
{% trans %}Please activate JavaScript to enable the search
functionality.{% endtrans %}
</p>
</div>
<p>
{% 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 %}
</p>
<form action="" method="get">
<input type="text" name="q" value="" />
<input type="submit" value="{{ _('search') }}" />
<span id="search-progress" style="padding-left: 10px"></span>
</form>
{% if search_performed %}
<h2>{{ _('Search Results') }}</h2>
{% if not search_results %}
<p>{{ _("Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.") }}</p>
{% endif %}
{% endif %}
<div id="search-results">
{% if search_results %}
<ul>
{% for href, caption, context in search_results %}
<li><a href="{{ pathto(item.href) }}">{{ caption }}</a>
<div class="context">{{ context|e }}</div>
</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endblock %}

@ -0,0 +1,12 @@
{%- if pagename != "search" %}
<div id="searchbox" style="display: none">
<h2>{{ _('Quick search') }}</h2>
<form class="search" action="{{ pathto('search') }}" method="get">
<input type="text" name="q" />
<input type="submit" value="{{ _('Go') }}" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
{%- endif %}

@ -0,0 +1,27 @@
{% macro doc(path, title) %}
{%- if pagename == path -%}
<li class="active">
{% else %}
<li>
{%- endif -%}
<a href="{{ pathto(path) }}">{{ title }}</a></li>
{% endmacro %}
<strong>Getting Started</strong>
<ul>
{{ doc("docs/install", "Installation") }}
{{ doc("docs/quickstart", "Quickstart") }}
{{ doc("docs/troubleshooting", "Troubleshooting") }}
</ul>
<strong>Configuration</strong>
<ul>
{{ doc("docs/configuration/setup", "Setup") }}
{{ doc("docs/configuration/server", "Server") }}
{{ doc("docs/configuration/client", "Client") }}
</ul>
<strong>Advanced</strong>
<ul>
{{ doc("docs/extras/multi-site", "Multiple Sites") }}
{{ doc("docs/extras/uwsgi", "uWSGI") }}
{{ doc("docs/extras/api", "API") }}
</ul>

@ -0,0 +1,3 @@
[theme]
inherit = basic
stylesheet = css/site.css

@ -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";
}

@ -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";

@ -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;
}
}

@ -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/)

@ -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;

@ -0,0 +1,5 @@
@mixin hide-text {
color: transparent;
font: 0/0 a;
text-shadow: none;
}

@ -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;
// }

@ -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;
}
}

@ -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;
}

@ -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;
}
}

@ -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;
}
}
}

@ -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);

@ -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;
}
}
}

@ -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 | <number>
@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);
}

@ -0,0 +1,3 @@
@mixin appearance ($value) {
@include prefixer(appearance, $value, webkit moz ms o spec);
}

@ -0,0 +1,6 @@
//************************************************************************//
// Backface-visibility mixin
//************************************************************************//
@mixin backface-visibility($visibility) {
@include prefixer(backface-visibility, $visibility, webkit spec);
}

@ -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));

@ -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"));

@ -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));

@ -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);
}

@ -0,0 +1,4 @@
@mixin box-sizing ($box) {
// content-box | border-box | inherit
@include prefixer(box-sizing, $box, webkit moz spec);
}

@ -0,0 +1,47 @@
@mixin columns($arg: auto) {
// <column-count> || <column-width>
@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) {
// <border-width> || <border-style> || <color>
@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);
}

@ -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);
}

@ -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');
}
}
}

@ -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;
}
}

@ -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;
}
}

@ -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;
}

@ -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;
}

@ -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})");
}

@ -0,0 +1,8 @@
@mixin perspective($depth: none) {
// none | <length>
@include prefixer(perspective, $depth, webkit moz spec);
}
@mixin perspective-origin($value: 50% 50%) {
@include prefixer(perspective-origin, $value, webkit moz spec);
}

@ -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;
}
}
}
}

@ -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})");
}

@ -0,0 +1,15 @@
@mixin transform($property: none) {
// none | <transform-function>
@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);
}

@ -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);
}

@ -0,0 +1,3 @@
@mixin user-select($arg: none) {
@include prefixer(user-select, $arg, webkit moz ms spec);
}

@ -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;
}

@ -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%;
// }
// }

@ -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;
// }

@ -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;
}

@ -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

@ -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;
}

@ -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;
}

@ -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);
}

@ -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;
}
}

@ -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;
}

@ -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;
}

@ -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);
}

@ -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;
}

@ -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;
}

@ -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;
}

@ -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;
}

@ -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";

@ -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";

@ -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;
}

@ -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;
}

@ -0,0 +1,3 @@
@function em($pxval, $base: 16) {
@return ($pxval / $base) * 1em;
}

@ -0,0 +1,7 @@
@mixin fill-parent() {
width: 100%;
@if $border-box-sizing == false {
@include box-sizing(border-box);
}
}

@ -0,0 +1,5 @@
@if $border-box-sizing == true {
* {
@include box-sizing(border-box);
}
}

@ -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.";
}
}

@ -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;
}
}
}

@ -0,0 +1,8 @@
@mixin outer-container {
@include clearfix;
max-width: $max-width;
margin: {
left: auto;
right: auto;
}
}

@ -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;
}

@ -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;
}

@ -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;
}

@ -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;
}
}

@ -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;
}

@ -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;
}
}
}
}

@ -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);
}

@ -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));
}
}
}
}
}

@ -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;

@ -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;

@ -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; }

@ -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;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

@ -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
# "<project> v<release> 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 <link> 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

@ -0,0 +1,4 @@
Contribute
==========
To be writ.. wait.

@ -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
<script data-isso="/prefix/"
data-isso-css="true"
data-isso-lang="ru"
data-isso-reply-toself="false"
src="/prefix/js/embed.js"></script>
Furthermore you can override the automatic title detection inside
the embed tag, e.g.:
.. code-block:: html
<section id="isso-thread" data-title="Foo!"></section>
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
<script data-isso="/isso" src="/path/to/embed.min.js"></script>
data-isso-css
-------------
Set to `false` prevents Isso from automatically appending the stylesheet.
Defaults to `true`.
.. code-block:: html
<script src="..." data-isso-css="false"></script>
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`.

@ -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 <http://gunicorn.org/>`_ 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:

@ -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 <http://gunicorn.org/>`_ 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.

@ -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
-------------------------
...

@ -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 <http://gunicorn.org/>`_ 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.

@ -0,0 +1,57 @@
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:
.. 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

@ -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 <https://github.com/talkatv/talkatv>`_, written in Python.
Unfortunately, talkatv's (read "talkative") development stalled. Neither
anonymous nor threaded comments.
* `Juvia <https://github.com/phusion/juvia>`_, written in Ruby (on Rails).
No threaded comments, nice administration webinterface, but... yeah... Ruby.
* `Tildehash.com <http://www.tildehash.com/?article=why-im-reinventing-disqus>`_,
written in PHP. Did I forgot something?
* `Unobtrusive, self-hosted comments <http://stackoverflow.com/q/2053217>`_,
discussion on StackOverflow.

@ -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
<https://github.com/jgraichen/debian-isso/blob/master/debian/isso.service>`_
- SysVinit: `isso.init
<https://github.com/jgraichen/debian-isso/blob/master/debian/isso.init>`_
- OpenBSD: `GH:Gist <https://gist.github.com/noqqe/7397719>`_

@ -0,0 +1,187 @@
Quickstart
==========
Assuming you have successfully :doc:`installed <install>` 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 <configure-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 <configure-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: <https://disqus.com/>
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
<script data-isso="http://comments.example.tld/"
src="http://comments.example.tld/js/embed.min.js"></script>
<section id="isso-thread"></section>
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
<https://github.com/surfly/gevent/issues/295>`_ and `#299
<https://github.com/surfly/gevent/issues/299>`_ for details).
uWSGI
^^^^^
The author's favourite WSGI server. Due the complexity of uWSGI, there is a
:doc:`separate document <extras/uwsgi>` 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

@ -0,0 +1,4 @@
Troubleshooting
===============
To be written.

@ -0,0 +1,2 @@
Usage
=====

@ -0,0 +1,60 @@
{%- extends "layout.html" %}
{% set title = "Isso a commenting server similar to Disqus" %}
{% block extrahead %}
<script data-isso="//posativ.org/isso/api/"
src="//posativ.org/isso/api/js/embed.min.js"></script>
{% endblock %}
{% block header %}
<div class="index">
<figure class="duty-call">
<a href="http://xkcd.com/386/">
<img src="{{ pathto('_static/duty_calls.png', 1) }}" alt="XKCD Duty Calls"/>
</a>
<figcaption>by Randall Munroe, CC BY-NC 2.5</figcaption>
</figure>
<ul>
<li>
<p><strong>Create, Edit and Remove Comments</strong></p>
<p>Commenters can edit or delete their own comments (within
15 minutes by default).</p>
<p>Comments in moderation queue are not publicly visible until
activation.</p>
</li>
<li>
<p><strong>SQLite backend</strong></p>
<p>Because.</p>
</li>
<li>
<p><strong>Disqus Import</strong></p>
<p>You can migrate your Disqus comments without any hassle.</p>
</li>
<li>
<p><strong>client-side JavaScript</strong></p>
<p>Embed a single JS file, 54kb (18kb gzipped) and you are
done.</p>
<p>Supports Firefox, Safari, Chrome and IE10.</p>
</li>
</ul>
</div>
{% endblock %}
{% block body %}
<div class="links">
<h2>Links</h2>
<ul>
<li><a href="">Isso @ PyPi</a></li>
<li><a href="">Isso @ GitHub</a></li>
</ul>
<h2>Help</h2>
<p>
Join <a href="http://webchat.freenode.net/?channels=isso">
<code>#isso</code></a> on <a href="http://freenode.net/">Freenode</a>
or open an issue on <a href="https://github.com/posativ/isso">GitHub</a>.
</p>
{% include "searchbox.html" %}
</div>
<div class="demo">
<h2>Demo</h2>
<section id="isso-thread"></section>
</div>
{% endblock %}

@ -0,0 +1,4 @@
News
====
To be written.

@ -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
```
Loading…
Cancel
Save